While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.While updating the Semiconductor Toolkit, I looked for a better way to rebuild the Recall function.Semiconductor Toolkit を更新する際に、Recall 関数を再構築するより良い方法を探しました。
で配布プラットフォームを開きますJMP 。 右側の小さなボタンのグループ、具体的には「Recall」ボタンを見てください。 それはどこにでもあります。 JMP のほぼすべてのプラットフォームにこれがあります。 これは JMP 設計言語の DNA に組み込まれています。 ここで、毎日のデータとの闘いの最中に、その小さなボタンがどれほど頻繁にあなたの命を救ってくれたかを少し考えてみましょう。 たくさんありますよね? これはおそらく、JMP の機能の中で最も当然のことと思われている機能の 1 つです。 しかし、これは JMP をこれほど便利なものにする無数の小さな工夫の 1 つであると私は思います。
ここで、JSL スクリプタの皆さん、カスタム アドインでその Recall 関数を複製することについて少し考えてください。 これはかなり高度なタスクです。私がこれまで見てきたほとんどの人 (私自身を含む) のやり方では、呼び出したい GUI 要素ごとにカスタムまたはグローバル名前空間に変数を作成する必要があります。 次に、実行時に各 GUI 要素から情報を収集するサポート関数と、[Recall] ボタンが押されたときに情報を書き戻すサポート関数があります。それ。 は。 A. 王立。 痛み。JMP スクリプトとカスタム アプリケーションでリコール機能を設定することは、おそらく最も手間のかかる作業の 1 つです。 GUI 内の 1 つまたは 2 つのコンポーネントの場合、それは大したことではありませんが、標準プロセスは適切に拡張できません。 しかし、その努力に対して、リコール機能はアドインに洗練された使いやすさを与え、プロフェッショナリズムの新たなレベルに引き上げます。 アドインに呼び出し機能があると、ユーザーはそれを歓迎します。 そして、率直に言うと、私はコーディングが嫌いです。
再構築できます
少しバックアップさせてください。 この投稿で取り上げているアイデアは 2016 年初頭に始まりました。 ちょうど更新を始めたばかりだったのですが、半導体ツールキット(STK) をバージョン 2 に変更します。 別のブログ投稿で説明するさまざまな理由により、プロジェクト全体を に移行することにしました。アプリケーションビルダーでJMP 。これは、コード ベースと GUI に多くの手を加え、整理し、可能な限りコードを標準化し、同僚が提供したいくつかの新機能を追加することを意味しました (皆さん、改めて感謝します!)。 残念ながら、この作業により、バージョン 1 からの Recall の既存のコードがすべて壊れてしまいました。 アドイン内のすべてのプラットフォームの Recall コードを再構築するというタスクに直面して、私は次のように考えました。 反復的なコーディングをすべて削減する方法が必要です。」
同じ頃、私は STK の他の機能のために JSL で連想配列 (AA) を使用する方法を研究していました。 AA を使用している人はあまり見かけませんが、AA はスクリプト作成において非常に強力なツールとなり得ます。 この議論のために、AA は特別な種類のリストです。 検索したり、物を入れたり、取り出したりすることができます。 これらを特別なものにしているのは、リスト内の各エントリにキーと値という 2 つの部分があることです。 AA に関する素晴らしいブログ投稿やリソースがいくつかあります ( JMP ドキュメント連想配列のリストからデータテーブルへの変換についてJSL ヒント: リスト内の一意の値の検索) AA を扱うときに参照します。 彼らは AA に関する多くの資料をカバーしており、いくつかの素晴らしい例を持っています。 元の問題点に戻ると、グローバル変数名とその内容はおそらく AA のキーと値のペアに変換できるのではないかと思いつきました。 そこで、私の現在のミューズとして、連想配列を使用してより良い Recall を構築できないか考えました。
さて、私が考えていたことを理解するために、これが通常どのように行われるかを見てみましょう。 Recall を実装するには 2 つの基本操作が必要です。 ユーザーが何らかのトリガーアクションを実行するとき、つまり「OK」をクリックするとき、呼び出されるすべての GUI 要素、つまりリストボックス、ラジオボックス、テキストボックスなどは、何らかの方法でその内容を取得して保存する必要があります。 次に、ユーザーが「呼び出し」ボタンをクリックすると、保存されたコンテンツを GUI 内の適切な場所にプッシュバックする必要があります。 これら 2 つの操作の中心となるのは、次の 1 対の JSL メッセージです。 << get()そして<< セット() 。 GUI 要素に応じて、これらにはさまざまな種類があります (理由は聞かないでください) –私はJSLを書いていません–私はそのものを使っているだけです)が、それらは基本的にこれら 2 つのバリエーションにすぎず、同じことを行います。 JMP のスクリプト インデックス ([ヘルプ] > [スクリプト インデックス]) は、どの GUI 要素がどの要素に対応しているかを正確に把握するのに最適な方法です。
したがって、私の AA ベースのソリューションでは、多くのことを行う必要があります。 まず、使用できるようにする必要があります<< get()そして<< セット()すべての順列において。 どの GUI 要素を保存したいかを伝えることができなければなりません。 また、アドインの実行が終了しても消えないこの情報を保存する場所が必要です。 これは、改訂されたワークフローには、復元したい GUI 要素のリストを使用して AA を構築することが含まれることを意味します。 この AA をグローバルまたはカスタムの名前空間に入れる必要があります (これについては後ほど詳しく説明します)。 AA の構築について考えたときに、「AA のキーとして GUI 要素を使用する場合、キーと値のペアの値には何を使用すればよいでしょうか?」という疑問が生じました。 AA の仕組みにより、AA を空のままにしておくことができることに気付きました。つまり、「」を使用します。空の() 」を私の価値観として掲げていましたが、それはどういうわけか機会を無駄にしているように感じました。 これについてさらに考えてみると、GUI 要素のデフォルト値を AA のキーと値のペアに入れれば、実際に「リセット」機能 (GUI をデフォルトに戻す) を設定できることに気づきました。状態)同じコードを使用しています。 1 つの料金で 2 つの機能をサポートできます。 そして、優れたツーファーを愛さない人はいないでしょうか?
はい、名前空間は便利です
の問題に戻ります名前空間ちょっと。 名前空間について詳しくない人にとっては、名前空間は、学校でよく聞く神秘的で神秘的なもののように思えるかもしれません。 JMP ディスカバリー サミットまたは時々ブログ投稿で。 今すぐそのバブルをはじいてもらいましょう – それらはそれほど特別なものではありません。 ただし、これらは非常に便利であり、優秀な JSL スクリプタはそれらを効果的に使用する方法を学びました。 (誤解のないように言っておきますが、私はそのグループに自分自身を数えていませんが、SAS 本社やディスカバリー サミットで時々一緒にランチを食べます…だから、おそらくそれは何かに数えられるでしょう。)
名前空間とは何か、またその機能を理解するために、辞書を見てみましょう。 何かを書き込んでいる場合英語単語を調べる必要がある場合は、英語辞書。 もしあなたがそこで働き始めるとしたらフランス語単語を調べる必要がある場合は、フランス語辞書。 1066 年のノルマン征服のおかげで、同じ単語が両方の辞書に掲載されていても、意味がわずかに異なる場合があります。 書類を持っているので通常は問題ありませんコンテクストあなたを助けるために。 取り組んでいる場合フランス語文書に手を伸ばすと、フランス語辞書 – 問題ありません。 同じ文書内にフランス語と英語が混在していても、いくつかの情報があれば問題ありません。コンテクストどの辞書を参照すればよいかを理解するのに役立つヒントが得られます。これはJSLと何の関係があるのでしょうか? 名前空間は辞書のようなものです。 これらは、JMP に何を実行させたいのかを理解するための追加のコンテキストを提供するカスタム変数と関数を配置できる場所です。 たとえば、STK では、関数名を使用します。 annotateWindow()いくつかの場所で。 実際、私は既存のウィンドウに何かを描画するソフトウェアのあらゆる部分でこの名前の関数を使用しています。合計すると、おそらく次のような名前の関数が 6 個あります。 annotateWindow() 。 問題は、各バージョンの動作がわずかに異なることです。 ただし、それぞれが独自の名前空間に存在するため、これが問題になることはありません。 2 つの異なる GUI を同時に開いている場合でも。 JMP は、1 つの名前空間で作業しているときに期待されることを知っています。 annotateWindow()あることをするために、別の場所にいるときは、別のことをすることを期待します。 この戦略により、STK での GUI 設計プロセスが大幅に簡素化されました。 したがって、名前空間を使用すると、コンテクストJMP は、特定のスクリプトで使用する関数のバージョンを知ることができます。 これらは、それらを作成したアドインが閉じた後もメモリ内に存在する可能性があります。 これら 2 つの点が、リコール ソリューションに特に役立つのです。
コーディングの時間です
OK、理論は十分です。コードをハックしましょう。 それに、これの残りの部分は、説明しながら説明した方がわかりやすいでしょう。 まず、私はスクリプトでライブラリを使用することを支持しています。これは、ほとんどの開発状況でのベスト プラクティスです。 ほとんどのプログラミング言語でこれが可能です。JSL でも可能です。 したがって、私の Recall ソリューションはライブラリの形式で構築されます。 アプリケーションまたはスクリプトでライブラリを使用するには、開発者は単に含む()スクリプトの先頭に引数を指定し、ライブラリへのファイル パスを指定します。
2 番目に、私はおしゃべりな機能が大好きです。 スクリプトが各ステップで何を行っているかを示すのは非常に便利です。 しかし、おしゃべりな関数に次のようなことを伝えられるようにしたいとも思っています。黙れテストとコーディングが終わったら。 (注意しないと、おしゃべりな関数によりコードの実行が遅くなる可能性があります)。 見てみると実際の図書館ファイル交換では、大量の書き込みが見られます()そして見せる()引数。 これらは、 If(verbose == 1, <…>)を渡すことでそれらをオフにできるステートメント冗長 = 0関数へのコマンド。 私はスクリプトでこの構造をよく使用しており、通常は冗長なこれにより、1 つのパラメータを調整することでスクリプト全体の詳細ログのオンとオフを切り替えることができます。 コードを短くするために、以下のスニペットからこれらを削除しました。 完成したライブラリは次の場所にあります。リコール関数ライブラリ。
ライブラリ内の最初の関数が呼び出されますgenArrays() 。 この関数は、GUI 内の要素のリストとそのデフォルト値のリストから AA を作成します。 この関数は実際には AA のコピーを 2 つ作成します。1 つは Recall 関数をサポートするためのものです。 ::<プラットフォーム名>UserSettings 、もう 1 つはリセット機能をサポートするもので、 ::<プラットフォーム名>デフォルト。 これら 2 つの配列は Global 名前空間に保存されます。 ” ::変数名の先頭にある ” 演算子がこれを行います。 もう 1 つ注意すべき点は、私が使用していることです。 Eval(解析())構造。 これは動的 JSL を構築するための一般的な戦略ですが、置換スキームを使用すると面倒になる可能性があります。 最後に気づくことは、私はロックユーザー設定変数。 これは、AA またはその他の値が誤って上書きされるのを防ぐためです。
genArrays = Function( {objects, values, platformName, verbose = 1},
{Default Local},
// Create the defaults Array
defaultString = "::" || platformName || "Defaults = Associative Array( objects, values, Empty() )";
defaultStringName = "::" || platformName || "Defaults";
// Run the default command
Eval( Parse( defaultString ) );
// Create the defaults Array
userString = "::" || platformName || "UserSettings = ::" || platformName || "Defaults";
userStringName = "::" || platformName || "UserSettings";
// Try to create the user settings Array and lock it so it's not overwritten.
Try(
Eval( Parse( userString ) );
Eval( Parse( "Lock Globals( " || platformName || "UserSettings )" ) );
,
Write(
"\!N\!NThe Associative Array \!"" || platformName || "UserSettings\!" is already present in memory and ready for use.\!N\!N"
)
);
);
もう一度、AA を構築するために使用されるリストに何が含まれているかを見てみましょう。 この問題にどのようにアプローチするかを考えていたとき、いくつかの種類があるという問題に対処する必要がありました。 << get()そして<< セット()私が処理する必要があった引数。 私が思いついたのは、AA の Key-Value ペアの Key 部分にヘッダーを追加することでした。 これらは、キー (GUI 要素) の前に置かれる 1 つまたは 2 つの文字です。 を使用する GUI 要素の場合<< セット()ヘッダーはs^ 。 の<< アイテムを設定()そして<< テキストを設定()使用シ^そしてst^それぞれ。 GUI (「」と呼びます) があったとしたらきちんとしたGUI ”)をテキストボックス( TB1 )、ナンバーボックス(注1 )、およびリスト ボックス ( lb1 ) オブジェクトのリストは次のようになります。
objects = {st^tb1, s^nb1, si^lb1};
スクリプトの実行時、またはユーザーがリセット ボタンを押したときに、これらに特定の値を持たせたいと言う場合は、次のように設定できます。価値観私たちが望んでいたものをリストします。 を使用するつもりです空の()リストボックスの内容をクリアするコマンド。
values = {“hello world”, 42, Empty()};
2 つのリストができたので、次を使用できます。 GenArrays()次のような配列を生成する関数:
GenArrays(objects, values, “neatGUI”);
これにより、グローバル名前空間に 2 つの AA が作成されます。 作成時には、両方とも次のようになります。
[s^nb1=>42, si^lb1=>Empty(), st^tb1=>”hello world”]
ここで、私たちがしなければならないのは、次の 3 つの関数に同じ GUI 名を付けることだけです (きちんとしたGUI )あとはすべてやってくれます! 彼らが何をするのか見てみましょう。
実際に GUI と対話する最初の関数は次のとおりです。リセットロール() 。 この関数は基本的に、デフォルト AA の各キーと値を取得し、その情報を使用して GUI 要素をデフォルト値にリセットするループです。AA は「特別な」リストであるため、AA を操作するには別の命令を使用する必要があります。 したがって、添字を使用して AA の最初の値を取得する代わりに、 << 最初にメッセージ。 ここからは、キー内の GUI 要素からヘッダー部分を分離するだけです。これらの部分はいくつかのロジックに渡されるため、適切なロジックを使用します<< セット()現在のキーの AA からの値を含むメッセージ。 最後に、これはすべて別のメソッドを使用して実行されます。 Eval(解析())口論。 その後、AA 内のキーと値のペアごとにプロセス全体が繰り返されます。
resetRoles = Function( {platformName, verbose = 1},
{Default Local},
// Get the AA for the provided name
arrayName = Eval( Parse( "::" || platformName || "Defaults" ) );
arrayNameString = "::" || platformName || "UserSettings";
// Setup a logical break if an AA isn't provided
Try(
// Start the function
// Check if arrayName is an AA
If( Is Associative Array( arrayName ),
//Initialize the counter
currentKey = arrayName << First;
// Loop through the keys
For( i = 1, i <= N Items( arrayName ), i++,
// Extract the header and the key
stAA = currentkey; // The header-key pair
stType = Word( 1, stAA, "^" ); // The header part
stArg = Word( 2, stAA, "^" ); // The key part
// Match the operation header to the correct operation instruction and build the string
stArgString = Match( stType,
"si", stArg || " << Set Items( " || Char( arrayName[currentkey] ) || " )",
"s", stArg || " << Set( " || Char( arrayName[currentkey] ) || " )",
"st", stArg || " << Set Text( \!"" || Char( arrayName[currentkey] ) || "\!" )"
);
// Eval-Parse the string
Try( Eval( Parse( stArgString ) ) );
// Get the next key in the AA
nextkey = arrayName << Next( currentKey );
// set it as the target key for the next loop
currentkey = nextkey;
);
,
// Stop execution if not an AA.
Throw()
);
,
);
);
他の 2 つの関数は、ストアロール()そしてリコールロール() 、いくつかの小さな違いはありますが、同様に機能します。 まず、どちらもユーザーAAの代わりにデフォルトGenArrays によって作成された AA () 。 2番、ストアロール()用途<< get()メッセージを送信し、上書きします。価値GUI 要素の Key-Value ペアの一部なので、後で呼び出せるように保存されます。 編集中にユーザー AA をロック解除および再ロックするためのコードも含まれています。 最後に、recallRoles ()関数は<< 追加()とのメッセージシ^ヘッダーの代わりに<< セット()メッセージ。 コードをここに置くのではなく、以下を参照してください。ファイル交換のライブラリ。
そこで、少し考え、コーディングし、削除キーを自由に適用し (Windows の世界から抜け出せない人のためにバックスペースを…お悔やみを申し上げます)、さらにもう少しコーディングした結果、リコール/リセットをサポートする一連の関数を構築することが完全に可能であることがわかりました。連想配列を使用します。 今にして思えば、単純な 2 要素または 3 要素の GUI の場合、このプロセス全体は、確かに少し複雑です。ルーブ・ゴールドバーグ風。 ただし、10 または 20 の要素を持つ GUI を想像してください。 このアプローチは、従来のアプローチよりもはるかに拡張性に優れています。 呼び出し/リセットしたい内容とその初期値のリストを記録するだけで済みます。 そして、それを行うために、ひどいオーストリアなまりを誇示したり、火星の支配をめぐって権力に飢えた知事と戦ったりする必要さえありません。