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