Die Rückgabe eines Ausdrucks von einer Funktion unterscheidet sich, wenn ein Namespace verwendet wird.Hallo an alle,
Beim Zurückgeben eines Ausdrucks von einer Funktion bin ich auf ein seltsames Verhalten gestoßen und konnte bisher keine Diskussionen darüber finden. Ich habe die Angewohnheit, Namespaces für so ziemlich alles zu verwenden. Ich habe festgestellt, dass, wenn eine Funktion einen Ausdruck zurückgibt, der Ausdruck nicht für eine Funktion ohne Gültigkeitsbereich ausgewertet wird, sondern ausgewertet wird, wenn ein Namespace verwendet wird. Weiß jemand, was hier los ist?
Dieses Skript sollte zeigen, dass die Variable x nicht beim Aufruf der ersten Funktion, sondern beim zweiten Funktionsaufruf inkrementiert wird. Ich verwende JMP v17.0.0, daher wurde das Problem möglicherweise behoben, aber ich kann derzeit kein Update durchführen.
NewNamespace("varNamespace");
varNamespace:x = 0;<BR />
myFunc = Function({},
{DEFAULTLOCAL},
returnVal = Expr(varNamespace:x++);
NameExpr(returnVal);
);
myFunc();
Show(varNamespace:x);
NewNamespace("functionTest");
functionTest:myFunc = Function({},
{DEFAULTLOCAL},
returnVal = Expr(varNamespace:x++);
NameExpr(returnVal);
);
functionTest:myFunc();
show(varNamespace:x);Automatisierung und SkripterstellungBetreff: Die Rückgabe eines Ausdrucks von einer Funktion unterscheidet sich, wenn ein Namespace verwendet wird.Ich habe auf 17.2.0 aktualisiert und kann bestätigen, dass das Problem immer noch auftritt.
Hier ist ein vereinfachtes Beispiel, das hoffentlich leichter zu lesen ist
x = 0;
myFunc = Function({}, {DEFAULTLOCAL},
Expr(::x++);
);
myFunc(); // Returns unevaluated expression: Expr(::x++)
Show(::x); // ::x=0;
NewNamespace("funcNS");
funcNS:myFunc = Function({},{DEFAULTLOCAL},
Expr(::x++);
);
funcNS:myFunc(); // Returns evaluated expression;
show(::x); // ::x=1;Betreff: Die Rückgabe eines Ausdrucks von einer Funktion unterscheidet sich, wenn ein Namespace verwendet wird.Ich bin mir ziemlich sicher, dass Sie einen Fehler entdeckt haben. Hier ist eine noch vereinfachte Demo von zwei Möglichkeiten, Funktionen in Namespaces zu verwenden:
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
expr(1+2)
)
);
funcNS:f2 = Function( {},
expr(1+2)
);
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) );
Type(funcNS:f1()) = "Ausdruck";
Type(funcNS:f2()) = "Number";
f1 gibt die richtige Antwort zurück; f2 wertet die Antwort falsch aus. Ich bevorzuge den f2-Weg (abgesehen von diesem Fehler), weil er schneller ist:
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
1
)
);
funcNS:f2 = Function( {},
1
);
nf1 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf1++,
funcNS:f1()
);
Show( nf1 ); // 180K calls
nf2 = 0;
For( endtime = HP Time() + 1e6, endtime > HP Time(), nf2++,
funcNS:f2()
);
Show( nf2 ); // 260K calls
nf1 = 182239;
nf2 = 258929;
Die beiden Probleme hängen wahrscheinlich zusammen. Innerhalb des Namespace-Funktionsaufrufs angegebene Elemente werden erst erstellt, wenn sie verwendet werden (sie werden als Ausdrücke beibehalten). Das bedeutet, dass f1 nicht kompiliert wird, wenn der Namespace erstellt wird, sondern f1 bei jeder Verwendung kompiliert wird und daher langsamer ist.Es gibt also wahrscheinlich speziellen C++-Code, um f1 neu zu kompilieren und ihn dann auszuwerten. Ich vermute, dass derselbe Code für f2 ausgelöst wird, aber nicht neu kompiliert wird, weil f2 bereits kompiliert ist, und dann das Ergebnis von f2 falsch auswertet, was meistens ein primitiver Wert ist und niemand es bemerkt. Der zurückgegebene expr()-Wert wird in einen einfacheren Wert ausgewertet, was Ihnen auffällt.
Mehr sehen...
@julianisch - Ich bin mir ziemlich sicher, dass es sich um einen Fehler handelt. Ich glaube nicht, dass es je nach falschem Verhalten viele Namespace-Benutzer geben wird.
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich.Gab es hierzu ein Update?
Soweit ich das beurteilen kann, besteht dieses Verhalten in jmp 19 immer noch.
Es scheint definitiv so, als würde der Rückgabewert in der ns:f2-Definition zweimal ausgewertet:
ns = New Namespace("fns",
f1 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
);
f1 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f2 = Function({},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f3 = Function({},
x = Expr(1+3);
return(Name Expr(Name Expr(x)));
);
Show(f1(), ns:f1(), ns:f2(), ns:f3());
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = 1 + 3;
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = x;
Falls jemand anderes auf dieses Problem stößt, ist die schnelle Lösung wie im Beispiel vorgeschlagen: Wickeln Sie den Rückgabewert in "Name Expr" ein, bis das gewünschte Ergebnis
Das funktioniert nicht. Ich hatte ax aus früheren Läufen im globalen/hier-Bereich definiert. Es scheint keine Möglichkeit zu geben, einen Ausdruck aus einer Funktion wie ns:f2 zurückzugeben.
Dies scheint definitiv ein Fehler zu sein. Ich glaube nicht, dass irgendjemand erwarten würde, dass ein ns:f2 einen anderen Wert zurückgibt als ns:f1 oder f1. Der Funktionscode ist genau derselbe.
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich.Ich werde über das Support-Team einen Bericht erstellen.
TS-00235798
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich.Ich habe auch einen Bericht erstellt: TS-00235515
Die Antwort des Supports war, dass dies nicht als Fehler angesehen werde, auch wenn das Verhalten unerwartet sei.
Die Grundursache war wie von Ihnen beschrieben: Grundsätzlich wird die Funktion ns:f2 zweimal ausgewertet.
Dies scheint sich auch auf alles auszuwirken, was außerhalb der anfänglichen Namespace-Erstellung definiert ist, wie im aktualisierten Beispiel unten zu sehen ist.
Die vorgeschlagene Lösung besteht darin, alles in einen Ausdruck zu packen – ns:f2 = Expr(Function(....))
Names Default to Here(1);
delete symbols();
ns = New Namespace("fns",
{
f1 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
),
e1 = Name Expr(expr(1 + a))
}
);
f1 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f2 = Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
);
ns:f3 = Expr(Function({},{Default Local},
x = Expr(1+3);
return(Name Expr(x));
));
ns:e2 = Name Expr(expr(1 + a));
ns:e3 = Expr(Name Expr(expr(1 + a)));
show(ns:e1);
show(try(ns:e2, exception_msg));
show(ns:e3);
Show(f1(), ns:f1(), ns:f2(), ns:f3());
ns:e1 = 1 + a;
Try(ns:e2, exception_msg) = {"Name nicht aufgelöst: a"(1, 2, "a", a /*###*/)};
ns:e3 = 1 + a;
f1() = 1 + 3;
ns:f1() = 1 + 3;
ns:f2() = 4;
ns:f3() = 1 + 3;
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich.Diese Problemumgehung ist ... bedauerlich. Glücklicherweise versuchen die meisten Benutzer nicht, Ausdrücke zurückzugeben und benötigen sie nicht.
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich.Außerdem können die meisten unglücklichen Aspekte durch die Rückkehr gemildert werden
Ausdruck(Ausdruck(1+1))
anstatt die gesamte Funktion
Ausdruck(Funktion(...
funcNS = New Namespace(
"funcNS",
f1 = Function( {},
expr(1+2)
)
);
funcNS:f2 = Function( {},
expr(expr(1+2)) // <<<< extra expr wrapper
);
Show( Type( funcNS:f1() ), Type( funcNS:f2() ) ); // expr, expr
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich.Diese Lösung ist zwar eleganter, scheint jedoch nur zu funktionieren, wenn Sie den zurückzugebenden Ausdruck direkt schreiben können.
Wenn der Ausdruck in einer Variablen gespeichert ist und wir diese in ein Expr() einschließen, ist der zurückgegebene Wert ein Ausdruck mit dem Variablennamen
In diesem Fall scheint die einzige Lösung darin zu bestehen, den Rückgabewert in einen Name Expr() und die gesamte Funktion in einen expr einzuschließen.
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( funcNs:f4() ); // 1+2
Eine große Gefahr dabei ist, dass die zweite Auswertung außerhalb des lokalen Funktionsumfangs zu erfolgen scheint.
Eine globale Variable könnte den Rückgabewert überschatten, was keinen Fehler verursachen würde.
Names Default to Here(1);
Delete Symbols(); // Only use if you dont have variables you need
funcNS = New Namespace("funcNS");
funcNS:f1 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
);
funcNS:f2 = Function( {}, {Default Local},
x = Expr(1+2);
Expr(Expr(x));
);
funcNS:f3 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
x;
));
funcNS:f4 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Expr(x);
));
funcNS:f5 = Expr(Function( {}, {Default Local},
x = Expr(1+2);
Name Expr(x)
));
Show( Try(funcNs:f1(), exception_msg) ); // Name Unresolved
Show(funcNs:f2()); // X
Show(funcNs:f3()); // 3
Show(funcNs:f4()); // x
Show(funcNs:f5()); // 1+2
// Second Evaluation happens outside Function scope
x = Expr(Expr(1+10));
Show(funcNs:f1()); // 1+10
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich.Wow!
zu Tiny Traps in Jmp und JSL hinzugefügt
Betreff: Die Rückgabe eines Ausdrucks aus einer Funktion ist bei Verwendung eines Namespaces unterschiedlich. Names Default to Here(1);
x= Expr("Hello");
funcNS = New Namespace("funcNS");
funcNS:f = Function( {}, {Default Local},
x = Expr(1+2);
Substitute(Expr(Expr(ex)), Expr(ex), (Name Expr(x)))
);
Show( funcNS:f() , Type( funcNS:f() ) ); // expr, expr
- Geben Sie der Funktion die Möglichkeit, x innerhalb der Funktion nachzuschlagen: Nennen Sie Expr() statt Ausdruck().
- Um zu verhindern, dass der Namespace den Rückgabewert "zweimal" auswertet, muss der Rückgabewert mit Ausdruck() geschützt werden.
- Dies macht dem Name Expr() das Leben schwer
Ausdruck(Name Ausdruck()) verhindert, dass Name Ausdruck() seine Aufgabe erfüllt.
- In 99 % dieser Fälle kann man Eval Expr() verwenden.
Aber hier ist es schwierig, weil Ausdruck() in JMP für zwei Zwecke verwendet wird:
- „es ist hier()“ innerhalb von Eval-Ausdruck ()
- Ich schütze dich() - als Lösung für das Namespace-Problem
leider ist Eval-Ausdruck () mit dem ersten Ausdruck() zufrieden und wertet es aus.
daher funktioniert Eval Ausdr(Ausdr(Ausdr(Name Ausdr(x)))) nicht im Sinne von
Eval Expr (Ich beschütze dich (Es ist hier(Name Expr(x))))
stattdessen interpretiert es es als
Eval Expr( es ist hier(ich beschütze dich(Name Expr(x))))
und deshalb wird es zurückkehren
Ausdruck(Name Ausdruck(x))
... und dann sucht JMP nach x außerhalb des Funktionsumfangs
- Die ultimative Lösung für alle Ausdrucksprobleme ist Substitute()
Betreff: Ausdrucksbehandlung in JMP: Tipps und Falltüren
- is there an easier solution than
Substitute(Expr(Expr(ex)), Expr(ex), (Name Expr(x)))