Dynamicチュートリアル

このチュートリアルではDynamicDynamicModule,およびこれらに関連した関数に潜む原理を解説し,互いに,またWolfram言語の他の部分とどのようにインタラクトするかについての詳細まで言及する.

これらの関数は高レベル関数Manipulateの基本である.Manipulateは多数のインタラクティブな例題,プログラム,デモンストレーションすべてを,ひとつの非常に便利で比較的強固な構造で生成するというシンプルでありながら強力な方法を提供する.その構造で手元の問題が解けたら,それ以上のものは必要ないし,このチュートリアルを読む必要もない.しかし,複雑なユーザインターフェースを含む広範な構造を構築したい場合は,ぜひこのチュートリアルをお読みいただきたい.

これは実習型のチュートリアルである.つまり,説明の入力行を自分で実際に評価してみて,どのようになるかを見る必要がある.説明を読みながら入力を評価しないと,説明が理解できない.

Dynamicの基本原則

通常のWolfram言語セッションは一連の静的入力と出力で構成される.これは,入力された順に実行される計算を記録する.

下の4つの入力セルを順番に評価する.
In[1]:=
Click for copyable input
In[2]:=
Click for copyable input
Out[2]=
In[1]:=
Click for copyable input
In[4]:=
Click for copyable input
Out[4]=

現在 x は7であるが,最初の出力はまだ5のときの出力を表示している.もちろんこれは自分の実行してきた計算の履歴が見たい場合には非常に便利である.しかし,基本的に異なる種類の出力,現行の値を常に反映するよう自動的に更新されるような出力が欲しい場合もあるかもしれない.この新しい種類の出力を提供してくれるのがDynamicなのである.

次のセルを評価してみる.現在の x の値は7なので,結果は49になる.
In[5]:=
Click for copyable input

Dynamicでラップされた変数を含む入力を初めて評価すると,Dynamicなしで得られるのと同じ結果を得ることがほとんどである.しかしその変数の値を次々変化させると,表示される出力は遡及的に変化する.

下のセルを1度に1つずつ評価する.上に表示される値が変化することに注目.
In[6]:=
Click for copyable input
In[7]:=
Click for copyable input
In[8]:=
Click for copyable input

最初の2つの静的出力はまだそれぞれ25と49のままであるが,動的出力では値 x の最後の値の平方である100となっている.(x の値が再び変化すると,この記述は不正確となる.)

動的出力に入れることのできる値の種類に制約はない.x が最初に数であるというだけで,それが後続の評価で数式やグラフィックスになれないということにはならない.これは単純な機能のように見えるかもしれないが,非常に強力なインタラクティブ機能の集合の基礎となるものである.

x の値が変わるたびに,上記の出力は自動的に更新される(上にスクロールしていただきたい).
In[9]:=
Click for copyable input
In[10]:=
Click for copyable input
In[1]:=
Click for copyable input
Dynamic[expr]動的に更新された現在の expr の値として表示されるオブジェクト

基本的なDynamic式

Dynamicとコントロール

Dynamicはスライダーやチェックボックス等のコントロールと使われることが多い.Wolfram言語で使用できるコントロールすべてについては,コントロールオブジェクトで説明する.ここではスライダーを使い,どのようなことができるかを説明する.他のコントロールでDynamicを使用する原則は,基本的には同じである.

スライダーはSlider関数を評価すると生成される.この関数では第1引数は位置を,オプショナルの第2引数は範囲と刻み幅を指定する.デフォルト範囲は0から1であり,デフォルトの刻み幅は0である.

これは中央に位置するスライダーである.
In[11]:=
Click for copyable input
Out[11]=

つまみをクリックして動かしてみる.つまみは動くが,スライダーは何にも接続されていないので,何も起らない.

これは,スライダーの位置と変数 x の現在の値とを関連付ける(この形式は後で詳述する).
In[12]:=
Click for copyable input
Out[13]=
先ほどの動的出力はすでにもうスクリーン上には見えなくなっているので,ここで新しい x の動的出力を作成する.
In[14]:=
Click for copyable input
Out[14]=

最後のスライダーをドラッグする.スライダーが動くにつれ,x の値が変化し,動的出力がリアルタイムで更新される.

スライダーも x の値の変化に反応する.

このことを確かめるために,下の行を評価する.
In[15]:=
Click for copyable input

スライダーがジャンプし,それと同時に x の動的出力が変更される.

もう1つ x スライダーを作る.
In[16]:=
Click for copyable input
Out[16]=

2つのスライダーのうちのどちらかを動かすと,もう1方が完全に同調して動く.両方が x の現在の値に動的に,そして両方向的に連結しているのである.

Dynamicとその他の関数

DynamicSlider等のコントロールコンストラクトは,多くの点でWolfram言語の他の関数と同様に動作する. これらは出力,表,さらにタイプセットされた数式内のどこにでも現れる.これらの関数があるところはどこでも,リンクされている式または変数の現在値をリアルタイムで動的に表示および変更するという動作もある.Dynamicは簡単な基本要素であるが,Wolfram言語の残りの部分により,敏捷で面白いインタラクティブな表示を生成するための柔軟なツールへと変わるのである.

x スライダーの表を作る.これらは同時に更新される.
In[2]:=
Click for copyable input
Out[2]=
スライダーと現在値をひとつの出力にまとめることができる.
In[3]:=
Click for copyable input
Out[3]=
Dynamicの大きなパワーは,x のどのような関数も簡単に表示できるという点にある.
In[20]:=
Click for copyable input
Out[20]=
整数値のスライダーを使うと,動的に更新される代数式が生成できる.
In[21]:=
Click for copyable input
Out[21]=
PanelRowColumnGridおよびその他のフォーマットコンストラクトで動的式を使用することができる.
In[22]:=
Click for copyable input
Out[22]=

最後の例はManipulateの出力に似ている.Manipulateの出力は実際にDynamic,コントロール,フォーマットコンストラクトの組合せを生成し,これらの低レベル関数を使ってできることと基本的に差がないため,出力が似ていても不思議ではない.

動的出力における変数の局所化

これは簡単なプロットと連動するスライダーの別の例である.
In[23]:=
Click for copyable input
Out[23]=
別の関数に接続したスライダーである.
In[24]:=
Click for copyable input
Out[24]=

この両方の出力が可視であるときにどちらかのスライダーをドラッグすると,スライダーが互いに連動するのが分かる.一方の例でスライダーを動かすと,他方の例のスライダーも動く.これは両方の例題で大域変数のx が使われているためである.これが便利なこともあるが,ほとんどの場合それぞれが独立して動いた方が好ましい.このようなときに使うのが関数DynamicModuleである.

DynamicModule[{x,y,},expr]expr におけるDynamicオブジェクトのすべての評価の過程で,同じローカルのシンボル x, y, を維持するオブジェクト
DynamicModule[{x=x0,y=y0},expr]x, y, の初期値を指定する

Dynamicオブジェクトに対する変数の局所化と初期化

DynamicModuleModuleと同じ引数を持ち,変数の局所化と初期化のために同様に使われる.しかし操作方法には大きな違いがある.

x の「プライベート」な値を持つ2つの同じ例である.
In[25]:=
Click for copyable input
Out[25]=
2つの例が互いに独立して動作するようになった.
In[26]:=
Click for copyable input
Out[26]=
1つの出力に複数のDynamicModuleを入れてもよい.これらは出力のそれぞれの領域に関連付けられた変数の別々の値を維持する.
In[27]:=
Click for copyable input
Out[27]=

DynamicModuleの代りにModuleの方が使いたくなるかもしれない.事実その方が一見うまく動作しそうに見えるが,使わない方がよい理由がいくつかある.その詳細については「Dynamicの高度な機能」を参照されたい.

DynamicModuleはカーネルではなくフロントエンドで動作する.これは評価されても変化せず,出力としてフォーマットされると局所化を扱う出力式に埋め込まれる不可視のオブジェクトを生成する.出力のその空間が存在する(つまり,削除されない)限り,DynamicModuleを表す不可視のオブジェクトは変数の値を維持し,DynamicModuleのスコープ(領域)内での後続するDynamic式の評価において使うことができる.

DynamicModuleを含むノートブックを保存してからそのノートブックを閉じ,後で新しいWolframシステムセッションで開いてもすべての局所変数の値が保存されており,DynamicModule内のスライダーは同じ位置にあるということである.これはスライダーが大域変数と連結している場合(はじめの部分の例にあるように)や,DynamicModuleではなくModuleで局所化された変数に連結している場合には起らない.このような変数は現在のWolfram言語カーネルセッションに変数値を保存するのでWolframシステムを終了したとたん消失する.

出力の特定の領域に変数を局所化する以外に,DynamicModuleにはDynamicModuleを含む式が開いたときに自動的に関数定義を初期化するオプションや,式が閉じたり削除されたりしたときに値をクリーンアップするオプションがある.詳細はDynamicModuleに記載されている.

Dynamicの第2引数

Dynamic接続はデフォルトで双方向である.変数に接続したスライダーは,両方とも同じ変数の値を反映し制御するので,同時に動く.スライダーのつまみをドラッグすると,システムが という形式の式を構築し,評価する.ここで exprDynamicの第1引数で与えられた式,new はスライダーのつまみをドラッグしていった位置により決定される提案された新しい値である.割当てが実行できる場合は,新しい値は受容される.割当てが失敗すると,スライダーは動かない.

最初のスライダーを動かすと,もう一方のスライダーが反対方向に動く.しかし,2つ目のスライダーを動かそうとすると,新しい値を式に割り当てられないため,エラーが生じる.
In[1]:=
Click for copyable input
Out[1]=

Dynamicの第1引数には任意の式を保存することができるが,動的に実行された評価を変更するためにはオプショナルの第2引数を使う.これは第1引数の変数値を更新する「逆関数」を指定するのに便利である.Wolfram言語はDynamicの第1引数から自動的にそのような逆関数を引き出そうとはしない.自分で与えなければならない.

Dynamic[expr,f]val のインタラクティブな変更あるいは編集の間に連続的に を評価する

逆関数

以下は x の値をどのように更新するかを指定し,2つ目のスライダーをインタラクティブにする.両方のスライダーが動かせ,他方のスライダーは反対方向に動いて反応する.
In[30]:=
Click for copyable input
Out[30]=

2つ目のスライダーで動的に実行される式は純関数であり,これに提案された新しい値がで与えられる.この関数は,変更したい変数への割当てをすべて実際に行うものである.従ってが変更したいからといって単純にとはいえないのである.

マウスの位置とWolframシステムの状態の間に任意の関数を介入させられるということは非常にパワフルであり,簡単な逆関数以上の目的にも使うことができる.第2引数で与えられる関数は実質的に自由に何でもできる.

これは,つまみが丸め数の一定の許容度内であれば,スライダーを整数値に留める「戻り止め」を定義する.
In[31]:=
Click for copyable input
Out[31]=
以下では,変数が小数ではなく有理数(整数の分数)を取るようにする.
In[32]:=
Click for copyable input
Out[32]=

追跡動作を完全に制御するために,マウスでスライダーのつまみをクリックする最初,途中,最後で呼ばれる関数を別々に指定することができる.慣用的なユーザインターフェースプログラミングに精通している人なら,マウスダウン,マウスドラッグ,マウスアップの各イベントに対する個別の高レベルイベント関数として認識できる.

ここではクリック・ドラッグ操作中に背景色を変える.
In[33]:=
Click for copyable input
Out[33]=

Dynamicの第2引数を使っても,スライダーの動きを制限したり効率よく幾何制約を実装したりすることができる.

このSlider2Dのつまみは,円形にしか動かせない.
In[34]:=
Click for copyable input
Out[34]=

Dynamicの式の中での位置

Dynamicの基本動作は,入力式を出力セルにコピーすることである.正確には,Dynamicには属性HoldFirstがあり,評価しても変化しない.

Dynamic[x+y]を評価した結果はDynamic[x+y]である.これは出力のInputForm表現を調べると分かる.
In[35]:=
Click for copyable input
Out[35]//InputForm=

通常の出力でDynamicを見ることはない.それはDynamic[x+y]がフロントエンドの表示用にフォーマットされると,評価されない入力()のコピーを含むがその式の評価された値として表示されるオブジェクトで表されるからである.Dynamicラッパーはまだ出力に存在するが,不可視なのである.

Dynamicはフロントエンドのみで動作するので,式の値にアクセスしなければならないような関数の内部では使えない.

このような式では使える.
In[36]:=
Click for copyable input
Out[36]=
しかし,こちらでは使えない.
In[37]:=
Click for copyable input
Out[37]=

Plot コマンドはプロットを生成するために x に対して特定の数値を持っていなければならない.しかしプロットされる関数内部のDynamic[x]はカーネルで何にも評価されない.これはDynamic[x]として不活性のままで,Plot コマンドが動作しないようにする.

Plotコマンド内部の式は出力のどこにも直接現れないということもいえる.Dynamicはカーネルではなくフロントエンドで動作するフォーマット関数であるため,出力として置かれることのないところで使われると,誤りとなる.

Dynamicとコントロールを組み合せるときは,Dynamicを正しい場所に置くことが特に大切である.

この例は思ったように動く.スライダーを動かすと,x の値が変化する.
In[38]:=
Click for copyable input
Out[38]=
この例もうまくいくように見えるがスライダーを動かしても,x の値は変化しない.
In[39]:=
Click for copyable input
Out[39]=

これはSlider[x]をラップするDynamicが内容を評価するときに,x の値が代入される.その結果,第1引数が特定の数であり変数名が残らないスライダーとなる.この場合のスライダーは,静的スライダーの動的表示である.

ここで必要なのは,内部に変数の値への動的参照を含む静的スライダーである.コントロールの場合,どこにDynamicを置けばよいかということに簡単な規則がある.SliderCheckboxPopupMenu等のコントロール関数の第1引数はほとんど常にDynamic[var]である.

Dynamicが特定の位置で動かない場合以外は,Dynamicをどこに置くかについては非常に柔軟である.この関数は入力式の最も外側の関数として使われることがよくあるが,必要条件ではない.さらに高度なアプリケーションでは,Dynamicは通常式の深いところで使われネストすることさえできる.

x の値を10個複写した表を表示する.
In[40]:=
Click for copyable input
Out[40]=

Dynamicは式全体にラップされるので,Tableコマンドの評価は出力がノートブックに表示されるまで遅延される.x の値が変化するたびに,Tableコマンドは再評価される.

この例の出力は全く同じに見える.
In[41]:=
Click for copyable input
Out[41]=

しかし,ここではTable コマンドは即時に評価され10の別々のDynamic式を生成している.全体の結果がノートブックに出力された後でそれぞれの式が x を別々に評価する.

x が変更されると,最初の例ではカーネルに1つのリクエストを送りTable[x,{i,10}]の値を得る.一方2つ目の例ではカーネルに10個のリクエストを送り x の値を得る.最初の例の方が明らかに効率的に見え,この場合実際にそうである.しかし,あまりに多くのものを1つのDynamicにラップするという極端なことも避けなければならない.これも非効率的になる.

次で xy を初期化し,x の値に接続した新しいスライダーを設定する.
In[6]:=
Click for copyable input
In[7]:=
Click for copyable input
In[8]:=
Click for copyable input
Out[8]=
次は動的式を2グループ持つタブ形式である.両方 x の動的値(簡単な数値)と y の動的値(3Dプロット)を表示している.
In[9]:=
Click for copyable input
Out[9]=

ここでスライダーをドラッグすると,最初のタブの x の値が非常に速く更新されるのが分かる.これは,ほとんどのコンピュータでは基本的に瞬時に起こる.しかし更新は2つ目のタブでは遅い.それぞれのDynamic式は,最新の状態であるために,正確にいつ再評価されなければならないかということを追跡(非常に注意深く)しているのである.2つ目のタブでは,出力は x の値が変更されるたびに,大きく遅い3Dプロットを含む式全体を再評価するよう強制する.最初のタブでは,2つの別のDynamic式を使うことにより,実際には変化していない y は再評価せずに x の値が更新できる(次に進む前に最後の出力を削除した方がよい.大域変数 x がスクリーン上に見えている限り,それを含む例が遅くなるからである).

それぞれの場合にDynamicをどこに置けばよいかについて包括的に述べるのは難しいが,一般に小さい部分しか変化しない大きく複雑な出力を生成するときはDynamicはおそらくその部分だけをラップするようにした方がよい.反対に,値が変化する1つの変数に反応して出力のすべてあるいはほとんどが変化するような場合は,すべてのものをDynamicでラップした方がよい.

オプションにおけるDynamic

Dynamicは,オプション値が使用される前にフロントエンドに転送される場合,オプションの右辺に使うことができる.これはDynamic を式のどこに置けばよいかという「Dynamicの式の中での位置」の説明と関連した性質である.

プロットコマンドのPlotPointsのようなオプションは右辺にDynamicを取ることができない.プロットコマンドはプロットが生成される前に特定の数値を知る必要があるからである.Dynamicには式がフロントエンドに達するまで評価を遅らせる効果があるが,PlotPointsの場合はそれでは遅すぎ,値はすぐに必要となる.一方,フロントエンドで動作する関数のオプションは,通常有効にどちらかのオプション値にDynamicを使うことができる.

例えば,テキストのブロックの大きさを2つの方法で制御することができる.

DynamicStyle式全体をラップすることができる.
In[10]:=
Click for copyable input
In[11]:=
Click for copyable input
Out[11]=
あるいはDynamicFontSizeオプション値だけに入れてもよい.
In[59]:=
Click for copyable input
Out[59]=

Dynamicをオプション値に入れることには2つの潜在的な利点がある.まず,動的に生成される式が非常に大きい,例えば文書全体であるとする.Dynamicが式全体を囲んでいるときに必要なように,フォントの大きさが変わるたびに式全体をカーネルからフロントエンドに再転送するのは非効率的である.

もうひとつは,Dynamic式の出力は編集不可能であるということで(いつでも再生成されなければならないから),このことは最初の例の出力を編集不可能にしている.2つ目の例のテキストは,通常の静的出力なので,自由に編集できる.オプション値だけが動的なのである.

動的なオプション値もオプションインスペクタで設定できる.オプション値はセル,ノートブック,グローバルレベル,スタイルシートで設定できる(ただし,スタイルシートのような,値が多くのセルにより継承される位置で動的オプション値を設定した場合,パフォーマンスに大きく影響を与えることに注意).

SetOptionsで動的オプション値を設定することもできる.
In[51]:=
Click for copyable input
In[51]:=
Click for copyable input
ノートブックの背景色と大域変数 x とを連動させることで,背景色はスライダーかプログラムで制御できるようになる.
In[52]:=
Click for copyable input
Out[52]=
もちろんもとに戻れるというのもよい
In[53]:=
Click for copyable input

Dynamicと無限ループ

注意を払わないと,Dynamicを無限ループに投げ込んでしまうようなことが簡単に起る.

これはスクリーン上に見えている限り,できる限り速く昇順で数を数える.
In[54]:=
Click for copyable input

これはバグではない(しかし,いやならば上の出力を削除しても構わない).

無限ループの各サイクルの後で出力が更新されスクリーンが再描画されるので,これは実際非常に便利なものである.一般に,無限のDynamic更新が進んでいても,システムはタイプ,評価等に反応する.

また,ある点で変更を中止するこのような自己制動のDynamicを作ると便利である.

これは垂れたスライダーであり,どこにドラッグしても常にゼロに戻る.
In[55]:=
Click for copyable input
Out[55]=

CPU監視装置を起動していると,スライダーがドロップしている間にCPUにわずかの負荷がかかるのが分かる(主にスクリーン再描画のため).しかし一旦ゼロに達すると,負荷はなくなる.動的追跡システムにより,x の値が変化していないので,別の誰かが x の値を再び変更する(自分でスライダーをクリックしたとき等)まで,アップデートの必要がないということが分かる.「Dynamicの高度な機能」では動的追跡システムの仕組みをさらに詳しく説明する.

ヒント

Dynamicは属性HoldFirstを持つので,その第1引数は評価しない.これはDynamicの動作の基本であるが,多少予期せぬ動作を引き起す場合もある.

例えば, 数のリストがあり,それぞれの値を制御する1つのスライダーを作って,その値を変更したいとする.

リストと現在の値の動的表示を作ることから始める.
In[1]:=
Click for copyable input
In[2]:=
Click for copyable input
Out[2]=
次に,リストの各要素につき1つずつのスライダーの表を作る.それぞれのメンバーには を使ってアクセスする.
In[3]:=
Click for copyable input

驚くべきことに,これは動作しない.スライダーの周りにエラーが表示され,スライダーは動かず,上の動的出力は変更されない.コントロールではこのように部分抽出シンタックスが使えないと結論付けたくもなる.

変数i にはTableコマンドで一時的な値が与えられているが,DynamicHoldFirstであるためその値が使われないというのが問題なのである.

スライダーの表のInputFormを見ると,問題が明らかになる.
In[59]:=
Click for copyable input
Out[59]//InputForm=

ここで,保持された式の内部であっても変数 i に一時的な値を代入することが必要である.

これは/.を使ってもできるし,ここで示すようにいく分特異であるが便利な慣用的な形式を使ってもできる.
In[60]:=
Click for copyable input

この出力で,Dynamicは実は,非常に便利な特性である部分抽出シンタックスでも完全に動作することが分かる.

Dynamic内の遅い評価

Dynamicが評価を終えるのにかなり長時間かかる(あるいは数秒以上かかる)ような式をラップするのはよいことではない.

この例を評価すると出力$Abortedが表示されるまでに約5秒かかる.
In[61]:=
Click for copyable input

Dynamic出力が評価されるのを待つ間に,フロントエンドはフリーズし,入力その他の操作ができなくなる.通常の動的出力の更新はフロントエンドをロックするので,Dynamicの中に入れる式は比較的早く(1秒程度以内)評価されるものに制限することが大切である.幸い,コンピュータもWolframシステムも高速なので,複雑な2Dや3Dプロットを含む広範囲の関数も素早く簡単に評価することができる.

フロントエンドが永久にロックされるのを避けるために,動的評価は内部的にTimeConstrainedにラップされる.これはデフォルトでタイムアウト値が5秒(DynamicEvaluationTimeoutオプションで変更できる)になっている.極端な場合,TimeConstrainedは計算の放棄に失敗することがある.その場合数秒後にフロントエンドにダイアログボックスが表示され,不愉快な出力が削除されるまで動的更新を終了することができる.

Dynamicに遅いものがある場合にはオプションSynchronousUpdating->Falseを使うことができる.これにより動的要素はフロントエンドをロックしないで評価される.このような非同期のDynamicを評価している間にもフロントエンドは通常通りに動作するが,メインのShift+Enter評価キューはDynamicの評価で手一杯なので,それ以上のShift+Enter評価は,Dynamicが終了するまで評価を待つことになる(通常の同期のDynamicShift+Enter評価と干渉し合わない).

この例を評価すると,グレーのプレースホルダが10秒間表示され,その後結果が表示される.
In[62]:=
Click for copyable input
Out[62]=

重要なことは,10秒間の休止中にもフロントエンドで別のことが続行できるということである.

「Dynamicの高度な機能」では同期と非同期の動的評価の違いについての詳細を説明する.一般に,絶対に必要ではない限り,非同期の評価は使わない方がよい.非同期では迅速に更新しないし,技術的に不正確というのではないがコントロールや他の同期評価と非常に変な方法でインタラクトすることがある.

その他の参考文献

複雑な構造,特にネストしたDynamic式を含むものが使いたい場合は,DynamicおよびDynamicModuleの実装の詳細を理解するとよい.これについては「Dynamicの高度な機能」で説明してある.