Dynamicの高度な機能
「Manipulateチュートリアル」および
「Dynamicチュートリアル」では,関数
Manipulate,
Dynamic,
DynamicModuleから利用することのできる
Mathematica のインタラクティブな機能を使用するために必要なほとんどの情報を提供している.このチュートリアルでは,
Dynamicと
DynamicModule の仕組みについての詳細を説明し,インタラクティブで複雑な例題で最大のパフォーマンスを得るための高度な機能とテクニックも紹介する.
このチュートリアルの多くの例題では1つの出力値だけを表示し,遅い計算をシミュレートするために
Pauseを使っている.実際には,このようなものではなく便利な計算を実行したり,高度なグラフィックスや値の表を表示したりしているものと思っていただきたい.
これは実習型のチュートリアルである.説明の入力行を自分で実際に評価してみて,どのようになるかを見る必要がある.読み進みながら自分で入力を評価しないと,説明が理解できない.
ModuleとDynamicModule
Moduleと
DynamicModuleは同様のシンタックスを持ち,少なくとも一見多くの点で同様に動作する.しかしこの2つはいつ変数が局所化されるか,局所変数がどこに保存されるか,なぜ変数が一意なのかという点において本質的に異なる.
Moduleは局所変数のすべてを,新しく一意に構築された変数で置き換え,変数が現行の
Mathematica カーネルセッションでどの変数とも競合しないようにする.
局所化された変数の名前は,値を割り当てずにモジュールのコンテキストから「逃がす」ことにより見ることができる.
| Out[3]= |  |
|
局所変数は他の変数と同じように動的に更新することができる.
| Out[4]= |  |
|
| Out[9]= |  |
| Out[10]= |  |
|
どちらの例も変数
x の複製に別々の設定を許す,一見独立したスライダーを出力している.
Module内部のスライダーの問題点は,異なるカーネルセッションでたまたま局所化された変数名を共有する可能性があるという点である.従って,このノートブックを保存し後で開くと,スライダーはそのときたまたま同じ局所変数を持つ他の
Moduleの変数に接続する可能性がある.
一方,
DynamicModule内部のスライダーではこのようなことは起らない.
DynamicModuleはオブジェクトがフロントエンドで表示されるまで変数を局所化せず,そのフロントエンドのセッションに一意の局所的名前を生成するのである.局所化は,
DynamicModuleが出力として最初に生成されたときに起り,その後
DynamicModuleを含むファイルが開かれるたびに新たに繰り返されるため,異なるセッションで生成された例題の間で名前の衝突が起ることはあり得ない.
Moduleで生成された変数は純粋にカーネルセッションの変数である.カーネルセッションが終了すると,値は失われ回復できない.これに反し,
DynamicModuleは変数の値を保存する責任を負う出力セルに構造を生成するため,ファイルに保存することができる.この違いはやや分かりにくいものなので,2つの例で説明する.まず最初に,
DynamicModuleは
Moduleが持続したものだと見ることができる.
この例のモジュールは順に一連の式を評価し,1行から次の行へとすべての局所モジュール変数の値が保存される(明らかである).複合式にはいくつの行があってもよいが,はじめからなければならない.
Moduleの実行が終了すると,その局所変数すべてとともに消失する.
一方,
DynamicModuleは
DynamicModuleのボディ内部に現れる
Dynamicの式を評価すると,上の例の複合式の追加行のようになる環境を生成する.まるで別々の評価が複合式の別々の行であるかのように,1度の動的更新から次まで,すべての変数の値は
DynamicModuleで生成された局所変数コンテキストの中にすべて保存される.
この変数値の保存は,同じセッション内の後続の動的評価にだけではなく,今後すべてのセッションにまで拡張される.すべての局所変数値はノートブックファイルに保管・保存されているので,完全に新しい
Mathematica セッションでそのノートブックを開いても,値はそのままであり,終了したときと全く同じ状態から動的更新が再開する.
DynamicModuleは無限に拡張できる
Moduleのようなものである.
Moduleと
DynamicModuleの相違点についての別の見方として,
Moduleでは変数をある一定時間内(モジュールのボディが評価されている間)だけ局所化するのに対し,
DynamicModuleでは出力の空間の特定の領域に対して変数を局所化するというものがある.
出力空間が存在する限り,それに対して定義された変数の値は保存されるため,
DynamicModuleのスコープ(領域)内の
Dynamic式の後続の評価で使用することができる.出力をファイルに保存することで,その部分は休止状態に入り,再びファイルが開かれる瞬間を待つ(コンピュータサイエンスの用語では,フリーズドライとかシリアライズされたオブジェクトと言われることがある).
セッションをまたいで状態が維持できるという
DynamicModuleの機能は,ファイルの編集という概念を拡張するものでもある.通常ファイルのテキストや式を編集し,ファイルを保存して再び開くとき,編集した最後の状態で開くことを期待する.編集とはファイルの内容を変更することである.
通常のカーネル変数にはこの特性はない.
x に値を割り当て,
Mathematica を終了し再起動すると,
x はもうその値を持たない.これにはいくつか理由がある.そのひとつが
x の値をどこに保存するかという問題である.
DynamicModuleは特定の変数(局所変数)の値を保存する特定の場所(出力セル)を定義することでこの問題に答える.スライダーを動かす,入力フィールドにタイプする,動的なグラフィックスオブジェクトをドラッグする等の任意の編集操作により,局所変数の値は変化する.また,これらの値はファイルが保存されるときに自動的に保存されるので,スライダーおよびその他のオブジェクトは終了時と同じ状態で開く.従って,
DynamicModuleを使うと,テキストと式がノートブックファイルで編集し保存することができるのと同じように,どのような量でも編集が可能になるのである.
フロントエンド所有のDynamicModuleの変数値
Mathematica の通常の変数はカーネルが所有する.その値はカーネルの中にあり,フロントエンドにその値を表示するよう指示すると,値を取り出すためにカーネルとのトランザクションが始まる.通常の変数の値を参照する動的出力にもこれと同じことが言える.
1つのスライダーが動くと,その他の499もそれと同期に動く.これでは,
x の値を読み出すのにカーネルと500の別々のトランザクションが必要になる.(
Mathematica のセマンティックスは非常に複雑なので,
x を数回続けて評価して常に同じ値が実際に戻るという保証はない.カーネルから読み出された単独の値をすべてのスライダーと共有することにより,フロントエンドの効率を向上させるのは可能とはいえない.)
これとは異なり,
DynamicModuleで宣言された変数はフロントエンドが所有する.その値はフロントエンドにあり,フロントエンドで値が必要なときは,ほんのわずかなオーバーヘッドで局所的に読み出すことができる.
そのような変数に複雑な関数が適用されると,その値はもちろんカーネルに送られなければならない.これは透過的に起り,変数値の変化がジャストインタイムで両側のシステムに知らされる.
ある状況で通常のカーネル変数を使った方がよいか,
DynamicModule変数を使った方がよいかは,要因の数による.大切なのは,ノートブックが保存されるとき,すべての
DynamicModule変数の値はファイルに保存されるということである.セッションをまたいで保存される値が必要ならば,
DynamicModuleで宣言しなければならない.もう一方で,例えば数の大きい表を持つ一時的な変数は,ファイルの大きさを大幅に増加させる可能性があるので,
DynamicModule変数には向かない.
DynamicModuleの中に
Moduleをネストしたり,その逆にしたりするのは,フロントエンドとカーネルの間で変数を分割するのに合理的である.
多くの場合,パフォーマンスの制約となる要因は,カーネルから情報を取り出すのにかかる時間である.変数をフロントエンドに局所化することにより,スピードが劇的に向上することがある.
動的オブジェクトの自動更新
動的出力の指定は簡単である.
Dynamic[expr]は
expr を今評価した場合に得られる値を常に表示する.変数値,あるいはシステムのその他の状態が変更されると,動的出力も直ちに更新される.もちろん効率的な理由で,任意の変数が変更されるたびにすべての動的出力が再評価されるというわけではない.動的出力は,必要なときにだけ評価されるよう,依存性を追跡することが必要である.
| Out[9]= |  |
| Out[10]= |  |
|
はじめの式では,
a,
b,
c の値が変更されるたび,あるいは
a,
b,
c に関連付けられたパターンが変更されるたびに値が変化するかもしれない.2つ目の式は,
a が
Trueならば
c ではなく
a と
b に依存し,
a が
Falseならば
b ではなく
a と
c に依存する.
a が
Trueでも
Falseでもなければ,出力は
a のみに依存する(
If文が評価されずに戻るからである).
先験的にこのような依存性を見付けることは不可能(この効果の定理がある)なので,与えられた式を評価する過程で,実際にどの変数あるいは追跡可能な実体に遭遇したかを,システムが追跡する.その後,与えられた変数が新しい値を受け取ったときに,それを告知されなければならない動的式はいずれかを特定する変数とデータが関連付けられる.
システム設計上の大切な目標は,特に変数の値が素早く変更されている場合にでも,システムに絶対必要量以上の負荷をかけることなく,変数値を参照する動的出力により,変数値の監視を許可することである.
| Out[11]= |  |
|
動的出力が生成されると,それは評価され,シンボル
x は値が変化したときに更新する必要のある出力を見分ける情報でタグが付く.
ループが開始され
x に初めて新しい値が与えられると,それに関連付けられたデータが問い合され,動的出力を更新する必要があることがフロントエンドに知らされる.その後
x に関連付けられたデータは削除される.必然的に,システムは動的出力のことはすべて忘れ,ループにおける後続の割当ては
x の値を監視する動的出力があることによるスピードの遅延は全く起らない.
そのずっと後(コンピュータの時間スケール上の話であって,人間の時間スケールだと1秒足らずの間に),スクリーンが再描画され
x への参照を含む動的出力が再評価されると,動的出力と変数
x との間の接続が再び「認識」され,関連付けが再構築される.
その間ループはずっと実行し続けている.次回スクリーンが更新された後に割当てが終了すると,フロントエンドに別の知らせが送られ,プロセスが繰り返される.
デフォルトで,変数値の変化により引き起される動的出力は,最高で毎秒20回更新される(この割合は
SystemOptionの
"DynamicUpdateInterval"で変更できる).前の例では,各更新で値が数万,数十万倍跳ね上がるのが見られる(コンピュータのスピードが速ければ速いほど,この割合も大きくなる)が,計算の全体的なスピードはほんの1~2%しか遅くならず,マルチプロセッサシステムではほぼゼロに近い.
きついループで急速に変化しているシンボルの値を監視する動的出力があると,ループが格段に遅くなるように思うかもしれない.しかし,実際にはオーバーヘッドは,変数が変化する割合では0次であり,事実上通常最小限である.
動的出力はスクリーン上で可視なときにのみ更新される.この最適化により,プロセッサに多量の負荷を与えなくても絶え間なく変化する無数の動的出力を使うことができる.現在の文書の位置の上か下にある,スクリーンから見えない部分の出力は次回スクリーン上で見えるようになるまで評価されないままになる.スクリーンに現れたときに,表示される前に更新される.(従って,出力が更新されなくなるということは,副作用でもない限り通常はっきり分からないが,一般に副作用はない方がよい.)
動的出力は,変数以外のものに依存することもある.その場合,追跡も注意深く選択的に行われる.
以下は,スクリーン座標における現在のマウス位置を即座に更新して表示する.
| Out[14]= |  |
|
これが画面上に見えている限り,この動的出力はマウスの動きにつれて即座に再描画されるため,マウスが動くたびにある量のCPU動作がある.しかし,スクリーンから見えなくなると,CPU使用量はなくなる.
Refresh
通常動的出力は,システムにより更新する必要のある理由が検出されると常に更新される(詳細は
「動的オブジェクトの自動更新」を参照のこと).
Refreshを使って,何により更新が起るようにするかを明示的に指定することにより,この動作を変更することができる.
以下ではどちらかのスライダーが動いたときに,出力が更新される.
| Out[15]= |  |
|
Refreshで
TrackedSymbolsオプションを使うと,追跡するシンボルのリストを指定し,他の更新の理由をすべて無視することができる.
これは y の変更は無視し, x が変更されたときだけ更新する.
| Out[16]= |  |
|
2つ目(
y)のスライダーを動かすと何も起らないが,最初のスライダーを動かすと,両方の変数の現在値を反映するように式が更新される.2つ目のスライダーを動かした後の動的出力は,システムの現在の状態を反映していないので,誤っていると思うかもしれない.しかし,それこそが
Refreshコマンドが存在する必然的な理由なのである.
Refreshコマンドを使うと,動的出力が遅れないように常に更新しようとするシステムの動作をオーバーライドすることができる.
設定
TrackedSymbols->Automaticを使うと,
Refreshの第1引数で与えられた式で明示的(語彙的)に現れるシンボルだけを追跡することができる.例えば,
Refresh内では語彙的に起らない大域変数に依存する関数を使用すると,大域変数の値の変化により,通常ならば起るであろう更新が起らない.
Refreshは,一定間隔で更新を行うために使うこともできる.しかし,これはむやみに使ってはならない機能であることを覚えておかなければならない.
Dynamicの設計の基本は,更新が便利なときにだけ常に直ちに更新するので,決まったスケジュールで更新することは必要ないのである.しかし.そのどちらも実行できない,あるいはしない場合もある.
厄介になりそうなのは
RandomRealである.
RandomReal[]を評価するたびに異なる解が得られるため,
Dynamic[RandomReal[]]はできるだけ速く連続的に更新する必要があると思うかもしれない.しかし通常それは便利でないどころか,内部的にランダム性を使用する多数のアルゴリズムにとってマイナスの結果となる.例えば,
Dynamic内部のモンテカルロ積分法は,毎回わずかに異なる結果を出すというだけで,継続的に更新してはならない.
このような理由で,
RandomReal[]は更新を引き起さないという点で扱いにくくはない.新しい乱数が見たい場合は,
Refreshを使いどのくらいの頻度で出力を更新したいかを指定しなければならない.厄介ではない関数の別の例に,ファイルシステム操作がある.
| Out[17]= |  |
|
| Out[18]= |  |
|
BasicMathInputパレットを含むファイルの大きさが変わるという珍しい状況では,この
Dynamicは更新されない.ファイルの大きさを監視したい場合,
Refreshを使ってポーリングの間隔を指定しなければならない(十分に高度なオペレーティングシステムでは,理論的に
Mathematica が効率的にファイルシステムの動作についての知らせを受けるということは可能であり,将来の
Mathematica のバージョンでは実際そのような式を自動的に更新することになるかもしれない.他の
Dynamic式と同様に,自動修正が常にゴールである).
最後に,動的更新を引き起すように見えて実はそうでないという関数がいくつかある.例えば
DateListと
AbsoluteTimeである.
RandomRealと同様に,これらの関数が自動的に更新を引き起すことは,価値があるというよりも問題となる.
Refreshは自明に使い,時計のようなオブジェクトを生成することができる.関数
Clockは扱いにくい時間ベースの関数として特に意図されている.
| Out[19]= |  |
|
Refreshのネスト
上の例では,
Refreshは常に
Dynamicの内部で最も外側の関数である.なぜ
Refreshのオプションが
Dynamicのオプションでないのであろうかと思うかもしれない.しかし,実際,
Refreshは式のできるだけ深いところに置くことが大切なことがよくある.特に時間ベースの更新区間を指定する場合は特に大切である.
| Out[20]= |  |
|
チェックボックスをチェックすると,
Refreshは時計を頻繁に更新し,CPU時間は最新状態を保つために消費される.しかし,チェックボックスがチェックされていないと,評価が
Refresh式に到達できなくなり,出力は静的のままでCPU時間も消費されない.
Refreshが
Dynamic内部の式全体の周りをラップすれば,たとえ時間が表示されていなくてもCPU時間は継続して消費される.「No clock」という言葉は意味もなく常に更新される.(この更新は目に見えないものである.スクリーンのちらつきはないが,それでもCPU時間は消費される.)
Dynamicのネスト
Dynamic式もネストすることができ,システムは必要なときだけそれを更新する.
Dynamicの内容にさらにインタラクティブな要素が含まれている場合は特に,与えられた変数が変化したときに何が静的なままで何が更新されるかを記録することが大切である.
| Out[21]= |  |
|
最初のスライダーの位置は,その下にあるスライダーの数を決める.それぞれのスライダーは順にデータのリストの要素のうちのひとつの値に接続する.スライダーの数は変数であり,最初のスライダーの位置に対応して動的に変化するので,それを生成する表は
Dynamicの内部になければならない.
上の例は動作するが,今度はそれぞれのスライダーの隣にそれぞれの値をリストで表示したいとする.
| Out[22]= |  |
|
下部のスライダーのいずれかをクリックすると,1ステップだけ動いて止まる.これはグリッドの第2列の
data[[i]]式が,外側の
Dynamicで
dataの値への依存性を生成しているためである.
data が変更されるとすぐに,ドラッグしようとしているスライダーを含む外側の
Dynamicのコンテンツが破壊され,ほとんど同一のコピー(ここでは
data[[i]]の中の一つの表示された値が変更されている)で置き換えられる.換言すると,スライダーをドラッグするというアクションでそれが壊され,それ以上の動作ができないようになるのである.
このソリューションとして,式の中でデータが現れるときはすべて Dynamicでラップされるようにすることで,外側の Dynamicがデータの値に依存しないようにするというものがある.
| Out[23]= |  |
|
これでどのスライダーをドラッグしても,動的に更新された値が見られるようになる.これは外側の
Dynamicが,
data の値ではなくスライダーの数である
n の値にのみ依存しているためである.(技術的にいうと,
Dynamicは
HoldFirstなので,評価されると第1引数の式には評価が届かず,依存性がレジスタされないということである.)
複数レベルのネストされた
Dynamic式を使って,大きい複雑なインターフェースを構築する場合,気を付けなければならないことがある.
Mathematica は非常に複雑な場合でさえも,厳密に正しいことをしようとする.例えば,
Manipulateの出力は非常に複雑な内部関連のあるネストした
Dynamic式の集まりで構成されている.依存性の追跡システムが正しく動作しない場合,
Manipulateは正しく動作しない.
同期の動的評価と非同期の動的評価
Mathematica はフロントエンドとカーネルという2つの別々のプロセスからできている.これは実際にコンピュータサイエンスの言葉では別々のプロセスである.CPUタスクモニタに別々に表示される別のメモリ空間で実行される2つの独立したスレッドということである.
フロントエンドとカーネルはメインリンク,割込みリンク,サービスリンクとして知られる複数の
MathLink 接続を介して互いに通信し合う.メインおよび割込みリンクは,フロントエンドがカーネルに評価のリクエストを送り,カーネルが結果で反応する経路である.サービスリンクは逆向きに働く.カーネルがフロントエンドにリクエストを送る経路である.
メインリンクは
Shift+Enterの評価に使われる.フロントエンドは保留中の評価リクエストのキューを維持し,このリンクに送る.1つ以上の入力セルで
Shift+Enterを使うと,それらはすべてキューに加えられ,1つずつ処理される.どのときもカーネルは1つのメインリンク評価だけ(現在使用中のものがあればそれ)を扱う.その間,フロントエンドは完全に機能している.タイプしたり,ファイルを開いて保存したりすること等もできる.メインリンクの評価にどのくらいかかるかについての制限はない.終了するのに何日もかかるような評価を日常的に行う人もいる.
割込みリンクはフロントエンドが評価を送り答を得るという意味ではメインリンクと同様に動作するが,両側で全く異なる方法で管理される.フロントエンド側では割込みリンクは通常の
Dynamic更新を扱うために使われる.キューはない.その代り,フロントエンドは1度に1つの評価を送り,その他のタスクを続ける前に結果を待つ.従って,割込みリンクの評価を最大で数秒に制限することが大切である.割込みリンクの評価中,フロントエンドは完全に使われているため,タイプや他のアクションはできない.
カーネル側では,割込みリンクからの評価リクエストは,現在実行中のメインリンク評価(がある場合)を含むメインリンクからの評価より優先される.カーネルがメインリンク評価を実行中に評価リクエストが割込みリンクでくると,メインリンク評価は安全な点で(通常数ミリ秒以内に)中止される.割込みリンク評価は,終了するまで実行され,その後メインリンク評価が再開され,ぞれまでのように続行される.全体の効果は,スレッドメカニズムと同じではないけれど,よく似ている.複数の速い割込みリンクの評価は,1つの長く遅いメインリンクの評価中に実行され,カーネルが一度に複数の問題を処理しているという印象を与える.
割込みリンクの評価は,同時に実行しているメインリンク評価により使われている変数を含む,変数の値を変更することができる.ここでは矛盾は生じず,インターリービングは完全に安全な方法で行われる.何が起っているかを理解するまでは,それがかなり奇妙な動作に見えることもある.
| Out[24]= |  |
|
次のコマンドを評価する.評価が終了するまでの10秒間に,スライダーをランダムにドラッグする.
| Out[25]= |  |
|
スライダーが動く以外何も起っているように見えないが,2度目の評価が終了すると,
x がリストを構築するために評価された10個の点でのスライダーの位置を表す10個の
x の異なる値が記録されている.
Dynamicは通常,評価に割込みリンクを使う.評価は同期であり,終了するまでフロントエンドは動かなくなる.これが避けられない場合もあるが,次善最適となることもある.オプション
SynchronousUpdating->Falseを設定すると,フロントエンドに割込みリンクではなくメインリンクのキューを使わせることができる.そうするとフロントエンドはカーネルからの応答を受け取るまでグレイボックスのプレースホルダを表示するのである.
この例では,フロントエンドは正しいフォントサイズを描画するために Dynamic[x]の評価結果を知る必要があるので,デフォルト(同期の)更新が適切である.
| Out[26]= |  |
|
ここでは,2つ目の動的式が終了する前に出力セルが描画される.グレーボックスプレースホルダは結果が分かるまでの1秒間表示される.この例題を再評価すると再びグレーボックスが表示される.
| Out[27]= |  |
|
スライダーをクリックすると,1秒から10秒の遅れで表示が更新される.セルが
Shift+Enterで評価されているように,セルブラケットが選ばれる.これは評価がキューに入っており,評価が進行している間フロントエンドの作業が続行できるということを示している.
非同期の更新は,Dynamic部分式の周りにスクリーンを描画し後でその値を挿入することが可能な場合に,その部分式を完全に表示するのに便利である.これはWebブラウザがダウンロードを終了したときに遅れて挿入される画像の周囲にテキストを描画するのと同じである.
どうしていつも非同期の
Dynamic式を使わないのか.それにはいくつかの理由がある.最初の理由は,非同期の式はキューに入ると,定義上別の
Shift+Enter評価が行われている間,操作されないからである.これは通常の(同期)更新とは異なる.
| Out[29]= |  |
|
また,多くのコントロールはマウスのアクションに反応するために同期である必要がある.コントロールを非同期にすると,他のコントロールとの奇妙なインタラクションを引き起す可能性がある.
| Out[31]= |  |
|
スライダーを速く動かしてみると,ブツブツに切れた正弦波になる.これは
n の値が,
Tableコマンドの評価中に変更されたためである.これは正しい,予想通りの動作であるが,必要としていたのはおそらくこのような動作ではないであろう.
この問題は,同期の
Dynamic式を使うと起らない.通常
DynamicModule局所変数では起らず,非同期の評価を開始する前に変更される可能性のある変数の値を第2変数に保存することで避けることができる.
| Out[33]= |  |
|
ControlActiveとSynchronousUpdating→Automatic
一般的な規則として,スライダーやその他の連続動作のコントロールの動きにインタラクティブに反応することを意図した
Dynamicがある場合,1秒以下で(できればもっと短い時間で)評価できなければならない.評価にそれよりも長い時間がかかると,
Dynamicの更新が同期・非同期にかかわらず満足できるインタラクティブなパフォーマンスが得られない.
しかし,どうしても速く評価が終了できないが,スライダーには反応させたいというような例題の場合はどうしたらよいだろうか.ひとつの手段として,非同期の更新を使い,リアルタイムのインタラクティブなパフォーマンスが得られないものとあきらめるというものがある.これでよいならば,スライダーや他のコントロールで
ContinuousAction->Falseと設定するとよい.これでコントロールが解放された後に1度だけ更新され,所望の値に到達する前にドラッグの途中で長くなる可能性のある評価を開始することが避けられる.
スライダーを解放した後にだけ,セルブラケットが選ばれ評価動作を示す.
| Out[34]= |  |
|
これよりもっとよい解決策は,インタラクティブなコントロールのドラッグ操作の間に速く計算できるプレビューのようなものを与えておき,コントロールが解放されたときに時間のかかる完全な出力を計算するというものである.特にこれをサポートするためのいくつかの機能が提供されている.
まず,関数ControlActiveである.これは,コントロールが現在ドラッグされていれば第1引数を返し,されていなければ第2引数を返す.Dynamicとは異なり,ControlActiveはカーネルで評価され,その引数のひとつあるいは他方をすぐに返す通常の関数である.これは関数あるいはオプション値の内部に埋め込むことができる.
2つ目の機能は
Dynamicのオプション設定
SynchronousUpdating->Automaticである.これはコントロールがドラッグされているときには
Dynamicを同期にし,解放されると非同期にする.これら2つの機能を一緒に使うと,コントロールがドラッグされている間に使用するための同期に更新された表示を迅速に実行し,また,コントロールが解放されたときに時間のかかる非同期の更新を表示することができる.
スライダーをドラッグしているかどうかにより,表示されるテキストが異なる.
| Out[35]= |  |
|
スライダーをドラッグしている間は簡単な数字が同期で表示されるが,解放されるとグラフィックスが非同期に生成される.
| Out[36]= |  |
|
この例は,最終的な表示にどれだけの時間がかかるかにかかわらずフロントエンドは応答し続けることができ,プレビューと最終的な表示が全く異なることもあるということを示している.
もちろんほとんどの場合,プレビューは最終表示をある程度縮小したり,まばらにしたり,骨格だけにしたり,あるいは他を省略したりした形のものがよい.未処理であると滑らかなプレビューを与えられるほど速く,最終バージョンの計算に時間がかかってもフロントエンドはブロックしない.実際,この動作は非常に便利であるため,Plot3Dおよびその他のプロット関数でデフォルトとなっている.
ここではコントロールがドラッグされている間は非常に少数のプロット点を持つ3Dプロットを表示し,コントロールがリリースされたときに多数のプロット点で画像を精製する.
| Out[37]= |  |
|
Plot3Dでは,質の極端な広がりは少ないが,デフォルトで上と同じようなプレビューを作成する.
| Out[38]= |  |
|
| Out[39]= |  |
|
お気付きかもしれないが,上の3つの例題のいずれかの出力がノートブックに最初に置かれるとき,未処理の(コントロールアクティブ状態)画像が表示され,そのすぐ後に精錬された(コントロール非アクティブ)画像が表示される.これは意図的なものである.グレーのボックスではない何かが見られるように,システムが素早いプレビューを提供するのである.最初の更新は,コントロールがドラッグされたかのように同期で行われる.
このプレビュー評価の動作は次のセクションで詳しく説明する.
DynamicのImageSizeCache
ImageSizeCacheはDynamicのオプションであり,値がまだ計算されていないDynamicを表示するときに使われる長方形の大きさを指定する.これは通常入力で指定されずフロントエンドで自動的に生成され,Dynamic式とともにファイルに保存される.
ControlActive,
SynchronousUpdating,
ImageSizeCacheのインタラクションは微妙で複雑なものであり,非常に便利でもある.最初の2つのコンストラクトは
「ControlActiveとSynchronousUpdating→Automatic」で説明してある.ここでは残りの部分を説明する.
デフォルト値の
SynchronousUpdating->Trueを持つ
Dynamic式は,常に表示される前に計算され,一旦計算されると実際の画像の大きさが使われるため,その
ImageSizeCacheオプションの値は使われない.
しかし,
SynchronousUpdating→Falseである
Dynamic式は,初めて計算されている間,グレーの長方形として表示される.その場合,長方形の大きさは
ImageSizeCacheオプションの値により決まる.これにより,ノートブックの周囲の内容が正しい位置に描画され,
Dynamicが更新を終了したときに,ノートブックの内容の周りの不必要なちらつきやシフトがない(HTMLのユーザはこれが
imgタグの幅と高さのパラメータと似ていることにお気付きであろう).
ImageSizeCacheオプションは,
Dynamicの値の計算が成功するとシステムが自動的に設定するため,通常明示的に指定する必要はない(計算された結果は測定され,実際の大きさが
ImageSizeCacheオプションにコピーされる).自動的に計算されたこの値は,
Dynamic出力がファイルに保存されると,保持される.
| Out[40]= |  |
|
入力式が評価されると,小さいグレーの長方形が現れる.この
Dynamicは評価されたことがないので,その適切な画像サイズのキャッシュはなく,デフォルトの小さいサイズが使われる.
3秒後,結果が出て動的出力が表示される.この時点で実際のサイズが分かり
ImageSizeCacheオプションにコピーされる.この値は,出力セルの任意の場所をクリックし,メニューのを選ぶと見ることができる(これは,このセルを保存したときにノートブックに現れるように,セルを表す内在的な式を表示する).
ImageSizeCacheオプションがあることに注目されたい.
未加工のセル式の無難な場所にスペースをタイプし(セルの内容の再パースを強制するため),セルを再びフォーマットするためにを選んでみる.今回は最終の出力の大きさのグレーのボックスが3秒間表示された後で適切な出力が表示される.これは,前に保存された非同期の動的出力を含むノートブックを開いたときに表示されるものでもある.
設定
SynchronousUpdating->Automaticを使ったときの動作と似ているが,わずかに異なる点がある.
「ControlActiveとSynchronousUpdating→Automatic」の例題で見たように,
Automatic設定を使うと,出力が最初に置かれたときに同期のプレビュー評価が実行され,遅く非同期の値が計算される前に
Dynamic式の内容の迅速な表示を提供する.最初の評価は同期なので,グレーのボックスは表示されない.
しかし,このプレビュー評価は
ImageSizeCacheオプションが存在しないときだけ実行される.
SynchronousUpdating->Automaticと明示的な次元を指定する
ImageSizeCacheオプションを持つ
Dynamicは同期のプレビュー評価を行わず,最初の非同期評価の結果を待つグレーのボックス(正しいサイズで)を表示する.
これは最初,その実践的効果を考えるまでは,不可解な動作のように思える.一般に,
Dynamic式は,評価から出力として置かれて初めて現れたとき以外は常に
ImageSizeCacheオプション(フロントエンドで自動的に生成される)を持つ.ファイルから開かれたときはいつも既知のキャッシュされたサイズがある.
動的出力の大多数を扱う関数
Manipulateでは,デフォルト設定は
SynchronousUpdating->Automaticであり,記述された動作により,プレビュー画像が最初に生成されたときにその画像とともに出力をきれいに表示することができる.多数の
Manipulate出力を含むファイルを開くとき,Webブラウザで馴染みの便利な動作が使える.つまりテキストが直ちに表示され,グラフィックス(あるいは他の動的コンテンツ)は追って表示されていくのである.したがって,ファイルの最初のページが表示できる前に多数のプレビュー画像を前計算しなくてはならないことによって起る遅延なしに,ファイルを素早くスクロールすることができる.
Manipulate出力が最初の置かれたとき,初期評価が同期でなければ,サイズが未知であるために周囲のちらつきやリサイズ/シフトが起る.しかし,
Manipulate出力がファイルから開かれると,大きさが既知であるため,最終的な出力はちらつきなしでスムーズに表示できる.
ControlActiveの片側更新
ControlActiveはカーネルでの評価の後,それを含むDynamicの更新を発生させる.しかし非常に非対称であり,アクティブ状態から非アクティブ状態へと変化する場合のみである.非アクティブからアクティブへ変化しても,ControlActive自身では更新を引き起さない.
この幾分特異な動作の理由は,ControlActiveが完全に大域的な概念だからである.Mathematica のどこかにあるコントロールが現在ドラッグされているならば,アクティブな状態が返される.ControlActiveへの参照をたまたま含んでいる特定のDynamicと何の関係もないコントロールであっても同様のことが起る.ControlActiveが自身で更新を引き起すならば,どのコントロールをクリックしてもすぐにControlActiveへの参照を含むDynamic式(デフォルトの動的Plot3D出力等)は直ちに更新されるが,これは全く意味がない.そうではなく,更新する理由のある出力だけがControlActiveの現在の値を使うのである.
その一方で,コントロールが解放されたときに,最終的な調整された外観を与えるためにコントロールアクティブ形式で描画された出力を「修正する」ことが望ましい.このため,ControlActiveは非アクティブな状態になるときに,アクティブな状態で描画されたすべてのDynamic式を自身で更新する必要があるのである.
ControlActiveは自身で更新を引き起さないので,スライダーをドラッグしてもActive/Inactiveの表示が切り替わらない.
| Out[49]= |  |
|
動的出力の中の x が変化するので,このActive/Inactive表示は更新される.
| Out[41]= |  |
|
スライダーをクリックしたときに何が起るかを注意して見ていただきたい.マウスを動かさずにクリック・ホールドすると,表示は
Inactiveのままである.しかし,マウスを動かすとすぐに表示は
Activeへと更新される.これは
x が変化したことで
Dynamic全体が更新され,
ControlActiveの現在の状態を選んでいるために起っているものである.
マウスボタンを動かさずに注意して離してみる.ここで注目したいのは,
x が変化していないのに表示が
Inactiveに戻っているということである.
DynamicModuleワームホール
DynamicModuleで宣言された変数はノートブックの中の1つのセルで特別な長方形領域に局所化される.そのような局所変数のスコープを別のセルとか,別のウィンドウにまで拡張した方が望ましい場合もある.例えば,あるセルの中のダイアログボックスを開くボタンがあり,そのダイアログボックスを使うと,それを開いたボタンと同じスコープで宣言された変数値を修正することのできるという場合等である.
これは,
Mathematica の現実離れしたコンストラクトのひとつである
DynamicModuleワームホールを使って行うことができる.
DynamicModuleはオプション
DynamicModuleParentを取る.これの値は,フロントエンドのどこででも別の
DynamicModuleを参照する
NotebookInterfaceObjectである.変数の局所化のため,このオプションを設定した
DynamicModuleは,その2つが実際にどこにあるかにかかわらず(たとえ別のウィンドウにあろうと),参照されるものの中にあるかのように扱われる.
このようなワームホールの設定で厄介なのは,
NotebookInterfaceObjectが親
DynamicModuleを参照する必要があるようにすることである.この参照は
DynamicModuleが生成され出力に置かれた後でのみ生成でき,現行のセッションでのみ有効である.
この過程を簡単にするために,そして実際に明示的な
NotebookInterfaceObjectへの参照を避けるために,
DynamicModuleはオプション
InheritScopeも取る.そうすると
DynamicModuleParentオプションの現在の値が自動的に生成され,新しい
DynamicModule関数を,それが生成されるもととなった
DynamicModuleのスコープ内にあるかのように生成する.これはややこしいので例題を示す.
これを評価すると,+ボタンと数字の出力が生成される.
| Out[42]= |  |
|
+ボタンをクリックすると,
DynamicModule局所変数の値が増加する.これは出力の最後に表示される.数を減らすためには
Make - Paletteボタンをクリックしなければならない.これで新しい(非常に小さい),-ボタンを含むパレットウィンドウが表示される.
この-ボタンは,
DynamicModuleの
InheritScopeオプションによって生成されたワームホールに存在する.ボタンをクリックすると,別のウィンドウの遠い
DynamicModuleのスコープ内の局所的なプライベートの変数の値が減少する.
InheritScopeは,2つ目の
DynamicModuleを生成するコードがボタン内,あるいは最初の
DynamicModule内に位置する他の動的オブジェクト内から実行されたときにのみ使われる.明示的に
DynamicModuleParentを使って,既存の
DynamicModuleを連結することができるが,それは厄介であり,このドキュメントの範疇を超える.