當腳本編寫者嘗試使用變數作為 JSL 函數或訊息中的參數但未獲得預期結果時,他們常常會感到困惑。雖然在 JSL 中腳本編寫者在許多情況下都會遇到這種情況,但使用者經常遇到的情況是當他們嘗試設定 Spec Limits 列屬性時。用作規格限值的值通常是在上一個步驟中從查找表中提取的,並將這些值分配給變數。收集所有限制值並將其指派給變數後,下一個邏輯步驟是使用這些變數來設定分析表中的列屬性。這是腳本編寫者可能嘗試的代表性範例。您可以將此 JSL 複製到腳本視窗中並執行它以進行後續操作。
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 )}
);
執行此腳本時,圖示會出現在 pH 列旁邊,指示已設定列屬性,按一下圖示可確認它是規格限屬性。

然而,當您打開“色譜柱資訊”對話方塊視窗查看規格限的值時,它們丟失了!

JMP做了什麼?它似乎有效,但只是部分有效!為什麼?
為了進行調查,我們可以使用 JSL 查看屬性設定。也許這樣做會提供線索。評估以下表達式,
Column(dt, "pH") << Get Property("Spec Limits");
結果將以下資訊寫入日誌:
{LSL( myLSL ), USL( myUSL ), Target( myTarget ), Show Limits( 1 )}
好吧,這就是我所指定的,但為什麼我看不到分配給變數的值而不是變數本身?這個問題的答案觸及了困惑的關鍵: JSL 並不總是對參數求值足夠多次來解析分配的值,並且對參數求值的次數因使用變數的情況而異。例如,在當前上下文中,JMP“解析”列表參數以了解有一些項目可指定規格限制屬性的不同方面。這是一種評價。在另一次評估中,JMP 會檢查清單中的項目以確定要在規格限值屬性中設定哪些限值(LSL、USL、目標等)。 JMP 根本不計算清單項目內提供的參數(myLSL、myUSL、myTarget)。 JMP 期望數值作為 LSL、USL 和 Target 的參數。當提供變數時,JMP 不會識別該變量,也不會嘗試解析該變數以確定關聯值。
那麼,我能知道在給定情況下一個論證會被評估多少次嗎?如果我需要更多評估(如上例所示),我該怎麼辦?由於我們無法存取 JMP 的原始程式碼,因此很難知道在給定情況下將執行多少次評估。然而,這並不重要,重要的是知道它是否足以滿足當前的使用。我做的第一件事就是嘗試並驗證!
在事先不了解規格限制訊息如何期望值的情況下,我完全按照上面所示的方式編寫腳本。接下來,我透過在「列資訊」對話方塊中查看規格限製或使用該列建立圖表來檢查是否設定了規格限制,以查看圖表參考線是否以應有的方式顯示。如果驗證失敗,就像在本例中一樣,我需要編輯表達式以刪除變數並對數值進行硬編碼。
Column( dt, "pH" ) << Set Property(
"Spec Limits",
{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
)
然後,我運行表達式來驗證問題是否與我使用的變數有關,而不是其他語法問題。在確認表達式提供了帶有硬編碼值的預期結果後,我知道我遇到了一種情況,即我的變數沒有被充分評估以獲取分配的值。因此,我需要讓 JMP 在運行時動態建立表達式,以便在計算表達式之前將變數值嵌入其中。
現在有多種方法可以預先計算變量,但我首選的方法是使用 Eval Expr() 函數。其他方法包括使用 Substitute() 函數或透過連接字串建立 JSL 表達式,然後解析並計算最終字串。到目前為止,我最不喜歡的方法是將表達式建構成字串。該程式碼非常難以閱讀,因為它是單色的,因此我失去了 JSL 編輯器提供的有用的語義格式,並且嵌入的引號需要轉義。更糟的是,程式碼很難調試和排除故障,因為我失去了評估所選程式碼片段的能力。
// 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 )}
)"
)
在這方面,使用 Substitute() 函數更好,但如果表達式很長,我發現很難在閱讀表達式的同時查看後面指定替換內容和位置的參數。
// 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
)
相較之下,Eval Expr() 函數允許內聯替換規範,並且由於函數的參數是一個表達式,因此 JSL 編輯器能夠使用語義格式顯示它。
那麼 Eval Expr() 是如何運作的呢?它是一個將表達式作為唯一參數並傳回一個表達式的函數。它在提供的表達式中搜尋標記 Expr()。然後,JMP 計算 Expr() 括號內的內容,並用回傳值取代整個標記。回到上面設定規格限制的範例,如果我用 Expr() “包裝”我的變數並將整個表達式放在 Eval Expr() 呼叫中,JMP 會將我的變數(和 Expr() 標記)替換為運行時變數值並傳回修改後的表達式。所以,
// 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 )} );
);
返回:
Column( dt, "pH" ) << Set Property(
"Spec Limits",
{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
)
那太棒了!我現在有一個表達式來設定規格限制屬性,其中包括所需的硬編碼值。我只是動態地編寫了 JSL 程式碼!現在我只需要評估這個修改過的表達式。為此,Eval() 函數很有用。我可以包裝 Eval Expr(),調用 Eval() 調用,並且 Eval Expr() 返回的表達式將立即計算。以下是一個完整的範例腳本,它將使用僅在運行時已知的限制值正確設定規格限制屬性:
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 )}
);
));
現在,當我驗證此腳本時,我會在「列資訊」對話方塊中看到「規格限」的值。

此外,評估這個表達式
Column(dt, "pH") << Get Property("Spec Limits");
結果將以下資訊寫入日誌:
{LSL( 6 ), USL( 8 ), Target( 7 ), Show Limits( 1 )}
最後,如果我使用該列建立圖表,我會在預期位置看到規格限制參考線。

動態編寫 JSL 程式碼對於腳本編寫者來說是一項有用的開發技能。在許多情況下,JMP 不會對表達式中的參數求值足夠多次來獲得所需的實際值。雖然動態編寫 JSL 程式碼的技術有多種,但我首選的方法是我所說的 Eval(EvalExpr(Expr())) 方法。它允許內聯替換和語義格式化,這使得閱讀和調試程式碼相對容易。