Dynamicチュートリアル
Dynamicの基本原則 | オプションにおけるDynamic |
Dynamicとコントロール | Dynamicと無限ループ |
Dynamicとその他の関数 | ヒント |
動的出力における変数の局所化 | Dynamic内の遅い評価 |
Dynamicの第2引数 | その他の参考文献 |
Dynamicの式の中での位置 |
このチュートリアルではDynamic,DynamicModule,およびこれらに関連した関数に潜む原理を解説し,互いに,またWolfram言語の他の部分とどのようにインタラクトするかについての詳細まで言及する.
これらの関数は高レベル関数Manipulateの基本である.Manipulateは多数のインタラクティブな例題,プログラム,デモンストレーションすべてを,ひとつの非常に便利で比較的強固な構造で生成するというシンプルでありながら強力な方法を提供する.その構造で手元の問題が解けたら,それ以上のものは必要ないし,このチュートリアルを読む必要もない.しかし,複雑なユーザインターフェースを含む広範な構造を構築したい場合は,ぜひこのチュートリアルをお読みいただきたい.
現在 x は7であるが,最初の出力はまだ5のときの出力を表示している.もちろんこれは自分の実行してきた計算の履歴が見たい場合には非常に便利である.しかし,基本的に異なる種類の出力,現行の値を常に反映するよう自動的に更新されるような出力が欲しい場合もあるかもしれない.この新しい種類の出力を提供してくれるのがDynamicなのである.
Dynamicでラップされた変数を含む入力を初めて評価すると,Dynamicなしで得られるのと同じ結果を得ることがほとんどである.しかしその変数の値を次々変化させると,表示される出力は遡及的に変化する.
動的出力に入れることのできる値の種類に制約はない.x が最初に数であるというだけで,それが後続の評価で数式やグラフィックスになれないということにはならない.これは単純な機能のように見えるかもしれないが,非常に強力なインタラクティブ機能の集合の基礎となるものである.
Dynamic[expr] | 動的に更新された現在の expr の値として表示されるオブジェクト |
Dynamicはスライダーやチェックボックス等のコントロールと使われることが多い.Wolfram言語で使用できるコントロールすべてについては,コントロールオブジェクトで説明する.ここではスライダーを使い,どのようなことができるかを説明する.他のコントロールでDynamicを使用する原則は,基本的には同じである.
スライダーはSlider関数を評価すると生成される.この関数では第1引数は位置を,オプショナルの第2引数は範囲と刻み幅を指定する.デフォルト範囲は0から1であり,デフォルトの刻み幅は0である.
DynamicとSlider等のコントロールコンストラクトは,多くの点でWolfram言語の他の関数と同様に動作する. これらは出力,表,さらにタイプセットされた数式内のどこにでも現れる.これらの関数があるところはどこでも,リンクされている式または変数の現在値をリアルタイムで動的に表示および変更するという動作もある.Dynamicは簡単な基本要素であるが,Wolfram言語の残りの部分により,敏捷で面白いインタラクティブな表示を生成するための柔軟なツールへと変わるのである.
最後の例はManipulateの出力に似ている.Manipulateの出力は実際にDynamic,コントロール,フォーマットコンストラクトの組合せを生成し,これらの低レベル関数を使ってできることと基本的に差がないため,出力が似ていても不思議ではない.
この両方の出力が可視であるときにどちらかのスライダーをドラッグすると,スライダーが互いに連動するのが分かる.一方の例でスライダーを動かすと,他方の例のスライダーも動く.これは両方の例題で大域変数のx が使われているためである.これが便利なこともあるが,ほとんどの場合それぞれが独立して動いた方が好ましい.このようなときに使うのが関数DynamicModuleである.
DynamicModule[{x,y,…},expr] | |
DynamicModule[{x=x0,y=y0},expr] | x, y, … の初期値を指定する |
Dynamicオブジェクトに対する変数の局所化と初期化
1つの出力に複数のDynamicModuleを入れてもよい.これらは出力のそれぞれの領域に関連付けられた変数の別々の値を維持する:
DynamicModuleの代りにModuleの方が使いたくなるかもしれない.事実その方が一見うまく動作しそうに見えるが,使わない方がよい理由がいくつかある.その詳細については「Dynamicの高度な機能」を参照されたい.
DynamicModuleはカーネルではなくフロントエンドで動作する.これは評価されても変化せず,出力としてフォーマットされると局所化を扱う出力式に埋め込まれる不可視のオブジェクトを生成する.出力のその空間が存在する(つまり,削除されない)限り,DynamicModuleを表す不可視のオブジェクトは変数の値を維持し,DynamicModuleのスコープ(領域)内での後続するDynamic式の評価において使うことができる.
DynamicModuleを含むノートブックを保存してからそのノートブックを閉じ,後で新しいWolframシステムセッションで開いてもすべての局所変数の値が保存されており,DynamicModule内のスライダーは同じ位置にあるということである.これはスライダーが大域変数と連結している場合(はじめの部分の例にあるように)や,DynamicModuleではなくModuleで局所化された変数に連結している場合には起らない.このような変数は現在のWolfram言語カーネルセッションに変数値を保存するのでWolframシステムを終了したとたん消失する.
出力の特定の領域に変数を局所化する以外に,DynamicModuleにはDynamicModuleを含む式が開いたときに自動的に関数定義を初期化するオプションや,式が閉じたり削除されたりしたときに値をクリーンアップするオプションがある.詳細はDynamicModuleに記載されている.
Dynamic接続はデフォルトで双方向である.変数に接続したスライダーは,両方とも同じ変数の値を反映し制御するので,同時に動く.スライダーのつまみをドラッグすると,システムが expr=new という形式の式を構築し,評価する.ここで expr はDynamicの第1引数で与えられた式,new はスライダーのつまみをドラッグしていった位置により決定される提案された新しい値である.割当てが実行できる場合は,新しい値は受容される.割当てが失敗すると,スライダーは動かない.
Dynamicの第1引数には任意の式を保存することができるが,動的に実行された評価を変更するためにはオプショナルの第2引数を使う.これは第1引数の変数値を更新する「逆関数」を指定するのに便利である.Wolfram言語はDynamicの第1引数から自動的にそのような逆関数を引き出そうとはしない.自分で与えなければならない.
Dynamic[expr,f] | val のインタラクティブな変更あるいは編集の間に連続的に f[val,expr]を評価する |
2つ目のスライダーで動的に実行される式は純関数(x=1-#)&であり,これに提案された新しい値が#で与えられる.この関数は,変更したい変数への割当てをすべて実際に行うものである.従ってxが変更したいからといって単純に(1-#)&とはいえないのである.
マウスの位置とWolframシステムの状態の間に任意の関数を介入させられるということは非常にパワフルであり,簡単な逆関数以上の目的にも使うことができる.第2引数で与えられる関数は実質的に自由に何でもできる.
追跡動作を完全に制御するために,マウスでスライダーのつまみをクリックする最初,途中,最後で呼ばれる関数を別々に指定することができる.慣用的なユーザインターフェースプログラミングに精通している人なら,マウスダウン,マウスドラッグ,マウスアップの各イベントに対する個別の高レベルイベント関数として認識できる.
Dynamicの第2引数を使っても,スライダーの動きを制限したり効率よく幾何制約を実装したりすることができる.
このSlider2Dのつまみは,円形にしか動かせない:
通常の出力でDynamicを見ることはない.それはDynamic[x+y]がフロントエンドの表示用にフォーマットされると,評価されない入力(x+y)のコピーを含むがその式の評価された値として表示されるオブジェクトで表されるからである.Dynamicラッパーはまだ出力に存在するが,不可視なのである.
Dynamicはフロントエンドのみで動作するので,式の値にアクセスしなければならないような関数の内部では使えない.
Plot コマンドはプロットを生成するために x に対して特定の数値を持っていなければならない.しかしプロットされる関数内部のDynamic[x]はカーネルで何にも評価されない.これはDynamic[x]として不活性のままで,Plot コマンドが動作しないようにする.
Plotコマンド内部の式は出力のどこにも直接現れないということもいえる.Dynamicはカーネルではなくフロントエンドで動作するフォーマット関数であるため,出力として置かれることのないところで使われると,誤りとなる.
これはSlider[x]をラップするDynamicが内容を評価するときに,x の値が代入される.その結果,第1引数が特定の数であり変数名が残らないスライダーとなる.この場合のスライダーは,静的スライダーの動的表示である.
ここで必要なのは,内部に変数の値への動的参照を含む静的スライダーである.コントロールの場合,どこにDynamicを置けばよいかということに簡単な規則がある.Slider,Checkbox,PopupMenu等のコントロール関数の第1引数はほとんど常にDynamic[var]である.
Dynamicが特定の位置で動かない場合以外は,Dynamicをどこに置くかについては非常に柔軟である.この関数は入力式の最も外側の関数として使われることがよくあるが,必要条件ではない.さらに高度なアプリケーションでは,Dynamicは通常式の深いところで使われネストすることさえできる.
x が変更されると,最初の例ではカーネルに1つのリクエストを送りTable[x,{i,10}]の値を得る.一方2つ目の例ではカーネルに10個のリクエストを送り x の値を得る.最初の例の方が明らかに効率的に見え,この場合実際にそうである.しかし,あまりに多くのものを1つのDynamicにラップするという極端なことも避けなければならない.これも非効率的になる.
ここでスライダーをドラッグすると,最初のタブの x の値が非常に速く更新されるのが分かる.これは,ほとんどのコンピュータでは基本的に瞬時に起こる.しかし更新は2つ目のタブでは遅い.それぞれのDynamic式は,最新の状態であるために,正確にいつ再評価されなければならないかということを追跡(非常に注意深く)しているのである.2つ目のタブでは,出力は x の値が変更されるたびに,大きく遅い3Dプロットを含む式{x,y}全体を再評価するよう強制する.最初のタブでは,2つの別のDynamic式を使うことにより,実際には変化していない y は再評価せずに x の値が更新できる(次に進む前に最後の出力を削除した方がよい.大域変数 x がスクリーン上に見えている限り,それを含む例が遅くなるからである).
それぞれの場合にDynamicをどこに置けばよいかについて包括的に述べるのは難しいが,一般に小さい部分しか変化しない大きく複雑な出力を生成するときはDynamicはおそらくその部分だけをラップするようにした方がよい.反対に,値が変化する1つの変数に反応して出力のすべてあるいはほとんどが変化するような場合は,すべてのものをDynamicでラップした方がよい.
Dynamicは,オプション値が使用される前にフロントエンドに転送される場合,オプションの右辺に使うことができる.これはDynamic を式のどこに置けばよいかという「Dynamicの式の中での位置」の説明と関連した性質である.
プロットコマンドのPlotPointsのようなオプションは右辺にDynamicを取ることができない.プロットコマンドはプロットが生成される前に特定の数値を知る必要があるからである.Dynamicには式がフロントエンドに達するまで評価を遅らせる効果があるが,PlotPointsの場合はそれでは遅すぎ,値はすぐに必要となる.一方,フロントエンドで動作する関数のオプションは,通常有効にどちらかのオプション値にDynamicを使うことができる.
Dynamicをオプション値に入れることには2つの潜在的な利点がある.まず,動的に生成される式が非常に大きい,例えば文書全体であるとする.Dynamicが式全体を囲んでいるときに必要なように,フォントの大きさが変わるたびに式全体をカーネルからフロントエンドに再転送するのは非効率的である.
もうひとつは,Dynamic式の出力は編集不可能であるということで(いつでも再生成されなければならないから),このことは最初の例の出力を編集不可能にしている.2つ目の例のテキストは,通常の静的出力なので,自由に編集できる.オプション値だけが動的なのである.
動的なオプション値もオプションインスペクタで設定できる.オプション値はセル,ノートブック,グローバルレベル,スタイルシートで設定できる(ただし,スタイルシートのような,値が多くのセルにより継承される位置で動的オプション値を設定した場合,パフォーマンスに大きく影響を与えることに注意).
SetOptionsで動的オプション値を設定することもできる:
注意を払わないと,Dynamicを無限ループに投げ込んでしまうようなことが簡単に起る.
無限ループの各サイクルの後で出力が更新されスクリーンが再描画されるので,これは実際非常に便利なものである.一般に,無限のDynamic更新が進んでいても,システムはタイプ,評価等に反応する.
また,ある点で変更を中止するこのような自己制動のDynamicを作ると便利である.
CPU監視装置を起動していると,スライダーがドロップしている間にCPUにわずかの負荷がかかるのが分かる(主にスクリーン再描画のため).しかし一旦ゼロに達すると,負荷はなくなる.動的追跡システムにより,x の値が変化していないので,別の誰かが x の値を再び変更する(自分でスライダーをクリックしたとき等)まで,アップデートの必要がないということが分かる.「Dynamicの高度な機能」では動的追跡システムの仕組みをさらに詳しく説明する.
スライダーの表のInputFormを見ると,問題が明らかになる:
これは/.を使ってもできるし,ここで示すようにいく分特異であるが便利な慣用的な形式を使ってもできる:
この出力で,Dynamicは実は,非常に便利な特性である部分抽出シンタックスでも完全に動作することが分かる.
Dynamicが評価を終えるのにかなり長時間かかる(あるいは数秒以上かかる)ような式をラップするのはよいことではない.
この例を評価すると出力$Abortedが表示されるまでに約5秒かかる:
Dynamic出力が評価されるのを待つ間に,フロントエンドはフリーズし,入力その他の操作ができなくなる.通常の動的出力の更新はフロントエンドをロックするので,Dynamicの中に入れる式は比較的早く(1秒程度以内)評価されるものに制限することが大切である.幸い,コンピュータもWolframシステムも高速なので,複雑な2Dや3Dプロットを含む広範囲の関数も素早く簡単に評価することができる.
フロントエンドが永久にロックされるのを避けるために,動的評価は内部的にTimeConstrainedにラップされる.これはデフォルトでタイムアウト値が5秒(DynamicEvaluationTimeoutオプションで変更できる)になっている.極端な場合,TimeConstrainedは計算の放棄に失敗することがある.その場合数秒後にフロントエンドにダイアログボックスが表示され,不愉快な出力が削除されるまで動的更新を終了することができる.
Dynamicに遅いものがある場合にはオプションSynchronousUpdating->Falseを使うことができる.これにより動的要素はフロントエンドをロックしないで評価される.このような非同期のDynamicを評価している間にもフロントエンドは通常通りに動作するが,メインのShift+Enter評価キューはDynamicの評価で手一杯なので,それ以上のShift+Enter評価は,Dynamicが終了するまで評価を待つことになる(通常の同期のDynamicはShift+Enter評価と干渉し合わない).
「Dynamicの高度な機能」では同期と非同期の動的評価の違いについての詳細を説明する.一般に,絶対に必要ではない限り,非同期の評価は使わない方がよい.非同期では迅速に更新しないし,技術的に不正確というのではないがコントロールや他の同期評価と非常に変な方法でインタラクトすることがある.
複雑な構造,特にネストしたDynamic式を含むものが使いたい場合は,DynamicおよびDynamicModuleの実装の詳細を理解するとよい.これについては「Dynamicの高度な機能」で説明してある.