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