Skripter sind häufig verwirrt, wenn sie versuchen, eine Variable als Argument in einer JSL-Funktion oder -Nachricht zu verwenden, aber nicht das erwartete Ergebnis erhalten. Es gibt zwar viele Situationen in JSL, in denen Skripter auf diese Situation stoßen, aber die häufigste Situation tritt bei Benutzern auf, wenn sie versuchen, die Spalteneigenschaft „Spezifikationsgrenzen“ festzulegen. Die als Spezifikationsgrenzen zu verwendenden Werte werden häufig in einem vorherigen Schritt aus einer Nachschlagetabelle extrahiert, wo sie Variablen zugewiesen werden. Nachdem alle Grenzwerte erfasst und Variablen zugewiesen wurden, besteht der nächste logische Schritt darin, diese Variablen zu verwenden, um die Spalteneigenschaft in der Analysetabelle festzulegen. Hier ist ein repräsentatives Beispiel dafür, was ein Skripter versuchen könnte. Sie können dieses JSL in ein Skriptfenster kopieren und ausführen, um den Vorgang nachzuvollziehen.
Names Default to Here(1);
// Open a sample data table
dt = Open("$SAMPLE_DATA/Cleansing.jmp");
// Values previously obtained from a lookup table and assigned to variables as:
myLSL = 6;
myUSL = 8;
myTarget = 7;
// Use the variables to set the spec limits column property
Column(dt, "pH") << Set Property(
"Spec Limits",
{LSL( myLSL ), USL( myUSL ), Target( myTarget ), Show Limits( 1 )}
);
Wenn dieses Skript ausgeführt wird, wird neben der pH-Spalte das Symbol angezeigt, das angibt, dass eine Spalteneigenschaft festgelegt ist. Durch Klicken auf das Symbol wird bestätigt, dass es sich um die Eigenschaft „Spezifikationsgrenzen“ handelt.

Wenn Sie jedoch das Dialogfenster „Spalteninformationen“ öffnen, um die Werte für die Spezifikationsgrenzen anzuzeigen, fehlen diese!

Was hat JMP gemacht? Es schien zu funktionieren, aber nur teilweise! Warum?!?
Um das zu untersuchen, können wir JSL verwenden, um die Eigenschafteneinstellungen anzuzeigen. Vielleicht liefert dies einen Hinweis. Wenn wir den folgenden Ausdruck auswerten,
Column(dt, "pH") << Get Property("Spec Limits");
führt dazu, dass die folgenden Informationen in das Protokoll geschrieben werden:
{LSL( myLSL ), USL( myUSL ), Target( myTarget ), Show Limits( 1 )}
Nun, da ist es, genau das, was ich angegeben habe, aber warum sehe ich nicht die Werte, die den Variablen zugewiesen wurden, statt der Variablen selbst? Die Antwort auf diese Frage bringt den Kern der Verwirrung ans Licht: JSL wertet Argumente nicht immer oft genug aus, um den zugewiesenen Wert aufzulösen, und die Häufigkeit der Argumentauswertung variiert je nach den Umständen, in denen Variablen verwendet werden. Beispielsweise „analysiert“ JMP im vorliegenden Kontext das Listenargument, um zu verstehen, dass es Elemente gibt, um verschiedene Aspekte der Eigenschaft „Spec Limits“ anzugeben. Das ist eine Auswertung. In einer anderen Auswertung überprüft JMP die Elemente der Liste, um zu bestimmen, welche Grenzen (LSL, USL, Target usw.) in der Eigenschaft „Spec Limits“ festgelegt werden sollen. JMP wertet die in den Listenelementen bereitgestellten Argumente überhaupt nicht aus (myLSL, myUSL, myTarget). JMP erwartet numerische Werte als Argumente für LSL, USL und Target. Wenn eine Variable bereitgestellt wird, erkennt JMP sie nicht und versucht nicht, die Variable aufzulösen, um den zugehörigen Wert zu bestimmen.
Kann ich also wissen, wie oft ein Argument in einer bestimmten Situation ausgewertet wird? Was kann ich tun, wenn ich mehr Auswertungen benötige, wie im obigen Beispiel? Da wir keinen Zugriff auf den Quellcode von JMP haben, ist es sehr schwierig zu wissen, wie viele Auswertungen in einer bestimmten Situation durchgeführt werden. Das ist jedoch weniger wichtig als zu wissen, ob es für die aktuelle Verwendung ausreicht . Als Erstes versuche ich, dies zu überprüfen!
Ohne vorheriges Wissen darüber, welche Werte die Spezifikationsgrenzen-Meldung erwartet, schreibe ich mein Skript genau wie oben anfangs gezeigt. Als Nächstes überprüfe ich, ob die Spezifikationsgrenze festgelegt wurde, indem ich sie im Dialogfeld „Spalteninformationen“ überprüfe oder ein Diagramm mit der Spalte erstelle, um zu sehen, ob die Diagrammreferenzlinien wie erwartet angezeigt werden. Wenn die Überprüfung fehlgeschlagen ist, wie in diesem Fall, muss ich meinen Ausdruck bearbeiten, um die Variablen zu entfernen und stattdessen die numerischen Werte fest zu codieren.
Column( dt, "pH" ) << Set Property(
"Spec Limits",
{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
)
Dann führe ich den Ausdruck aus, um zu überprüfen, ob das Problem an meiner Verwendung von Variablen liegt und nicht an einem anderen Syntaxproblem. Nachdem ich bestätigt habe, dass der Ausdruck mit den fest codierten Werten das erwartete Ergebnis liefert, weiß ich, dass ich auf einen Umstand gestoßen bin, bei dem meine Variable nicht ausreichend ausgewertet wird, um den zugewiesenen Wert zu erhalten. Daher muss ich JMP den Ausdruck zur Laufzeit dynamisch erstellen lassen, um die Variablenwerte darin einzubetten, bevor der Ausdruck ausgewertet wird.
Es gibt mehrere Möglichkeiten, eine Variable vorab auszuwerten, aber ich bevorzuge die Funktion Eval Expr(). Andere Methoden sind die Verwendung der Funktion Substitute() oder das Erstellen des JSL-Ausdrucks durch Verketten von Zeichenfolgen und anschließendes Parsen und Auswerten der endgültigen Zeichenfolge. Meine bei weitem am wenigsten bevorzugte Methode ist das Erstellen des Ausdrucks als Zeichenfolge. Der Code ist sehr schwer zu lesen, da er monochromatisch ist. Daher verliere ich die hilfreiche semantische Formatierung, die der JSL-Editor bietet, und eingebettete Anführungszeichen müssen maskiert werden. Noch schlimmer ist, dass der Code schwer zu debuggen und Fehler zu beheben ist, da ich die Möglichkeit verliere, ausgewählte Teile des Codes auszuwerten.
// An example of concatenating strings to dynamically write a JSL expression.
Parse(
"Column( dt, \!"pH\!" ) << Set Property(
\!"Spec Limits\!",
{LSL( " || Char(myLSL) || " ), USL( " || Char(myUSL) || " ), Target( " || Char(myTarget) || " ), Show Limits( 1 )}
)"
)
Die Verwendung der Funktion Substitute() ist in dieser Hinsicht besser, aber bei langen Ausdrücken finde ich es schwierig, den Ausdruck zu lesen und gleichzeitig die späteren Argumente zu betrachten, die angeben, welche Ersetzungen wo vorgenommen werden.
// An example of using Substitute() to dynamically write a JSL expression.
Substitute(
Expr(Column(dt, "pH") << Set Property(
"Spec Limits",
{LSL( lll ), USL( uuu ), Target( ttt), Show Limits( 1 )}
))
,
Expr(lll), myLSL,
Expr(uuu), myUSL,
Expr(ttt), myTarget
)
Im Gegensatz dazu ermöglicht die Funktion Eval Expr() eine Inline-Substitutionsspezifikation und da das Argument der Funktion ein Ausdruck ist, kann der JSL-Editor es mit der semantischen Formatierung anzeigen.
Wie funktioniert also Eval Expr()? Es handelt sich um eine Funktion, die einen Ausdruck als einziges Argument verwendet und einen Ausdruck zurückgibt. Sie durchsucht den bereitgestellten Ausdruck nach dem Token Expr(). JMP wertet dann aus, was sich in den Klammern von Expr() befindet, und ersetzt das gesamte Token durch den Rückgabewert. Um auf das obige Beispiel zurückzukommen, um die Spezifikationsgrenzen festzulegen: Wenn ich meine Variablen mit Expr() „umschließe“ und den gesamten Ausdruck in einen Eval Expr()-Aufruf einfüge, ersetzt JMP meine Variablen (und das Expr()-Token) zur Laufzeit durch die Variablenwerte und gibt den geänderten Ausdruck zurück. Also,
// An example of using Eval Expr() to dynamically write a JSL expression.
Eval Expr(
Column(dt, "pH") << Set Property(
"Spec Limits",
{LSL( Expr(myLSL) ), USL( Expr(myUSL) ), Target( Expr(myTarget) ), Show Limits( 1 )} );
);
Rückgabewerte:
Column( dt, "pH" ) << Set Property(
"Spec Limits",
{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
)
Das ist großartig! Ich habe jetzt einen Ausdruck zum Festlegen der Eigenschaft „Spec Limits“, der die gewünschten fest codierten Werte enthält. Ich habe gerade dynamisch JSL-Code geschrieben! Jetzt muss ich nur noch diesen geänderten Ausdruck auswerten. Dafür ist die Funktion Eval() nützlich. Ich kann Eval Expr() einschließen, einen Eval()-Aufruf einbinden und der von Eval Expr() zurückgegebene Ausdruck wird sofort ausgewertet. Hier ist ein vollständiges Beispielskript, das die Eigenschaft „Spec Limits“ unter Verwendung von Grenzwerten, die nur zur Laufzeit bekannt sind, korrekt einstellt:
Names Default to Here(1);
// Open a sample data table
dt = Open("$SAMPLE_DATA/Cleansing.jmp");
// Values previously obtained from a lookup table and assigned to variables as:
myLSL = 6;
myUSL = 8;
myTarget = 7;
// Use the variables to set the spec limits column property
Eval(Eval Expr(
Column(dt, "pH") << Set Property(
"Spec Limits",
{LSL( Expr(myLSL) ), USL( Expr(myUSL) ), Target( Expr(myTarget) ), Show Limits( 1 )}
);
));
Wenn ich dieses Skript jetzt überprüfe, sehe ich im Dialogfeld „Spalteninfo“ Werte für die Spezifikationsgrenzen.

Darüber hinaus wird dieser Ausdruck ausgewertet
Column(dt, "pH") << Get Property("Spec Limits");
führt dazu, dass die folgenden Informationen in das Protokoll geschrieben werden:
{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
Wenn ich schließlich ein Diagramm mit der Spalte erstelle, sehe ich die Referenzlinien der Spezifikationsgrenze an den erwarteten Positionen.

Das dynamische Schreiben von JSL-Code ist eine nützliche Fähigkeit für Skripter. Es gibt viele Situationen, in denen JMP ein Argument in einem Ausdruck nicht oft genug auswertet, um den tatsächlich benötigten Wert zu erhalten. Obwohl es mehrere Techniken zum dynamischen Schreiben von JSL-Code gibt, ist meine bevorzugte Methode die von mir so genannte Eval(EvalExpr(Expr()))-Methode. Sie ermöglicht Inline-Ersetzung und semantische Formatierung, was das Lesen und Debuggen von Code relativ einfach macht.