21

SaveDefinitionsの良いオプションですManipulate。これによりManipulate、作成に使用された定義が操作パネル内に保存されます。この方法で作成された操作は、空のノートブックにコピーでき、それでも単独で機能します。さらに、そのような多くの操作を含む作業中のノートブックも、開いたときにその下にエラーメッセージが印刷されたピンク色のボックスの急増にはなりません。素晴らしい!

しかし、このすべての良さには暗い面があり、気づいていないと本当に激しく噛む可能性があります。私はこれを数日間取り組んできたノートブックに入れましたが、問題を再現する段階的なおもちゃの例のシナリオを紹介します。

このシナリオでは、素敵な波状関数のプロットを表示するように作成したいManipulateので、これを定義します(このようなウィンドウサイズを作成してください。これは重要です)。

ここに画像の説明を入力してください

定義は素晴らしいので、次回のためにそれを保持し、初期化セルにします。次に、を追加してManipulate実行します。

f[x_] := x^2

Manipulate[
 Plot[n f[x], {x, -3, 3}],
 {n, 1, 4},
 SaveDefinitions -> True
 ]

すべてがうまく機能し、操作は本当に輝いています、それは良い日です。

ここに画像の説明を入力してください

あなたの妄想的な自己であるだけで、あなたは定義がOKであるかどうかをチェックします:

ここに画像の説明を入力してください

ええ、まだすべてがチェックアウトしています。罰金。しかし今、より良い波状関数は正弦であることがあなたに思い浮かぶので、あなたは定義を変更し、実行し、そしてパラノイアである、チェックしてください:

ここに画像の説明を入力してください

すべてはまだ大丈夫です。あなたはあなたの仕事を保存してやめる一日のハードワークから準備ができています。[カーネルを終了]

翌日。あなたは仕事を再開します。ノートブックの初期化セルを評価します。定義はまだ良いですか?小切手。

ここに画像の説明を入力してください

次に、[操作]ボックスまで下にスクロールし(のおかげで再実行する必要はありませんSaveDefinitions)、スライダーで少し遊んでください。そして、上にスクロールします。

ここに画像の説明を入力してください

あなたはパラノイアなので、もう一度fの定義を確認します。

ここに画像の説明を入力してください

見よ、誰かがあなたの後ろの定義を変えた!そして、In []番号( :def of f、first?、second?)Informationに従って、最初と2番目(?)のチェックの間に何も実行されませんでした。In[1]In[2]In[3]

どうしたの?もちろんManipulateです。AFullFormはその内部構造を明らかにします:

Manipulate[Plot[n*f[x],{x, -3, 3}],{{n, 2.44}, 1, 4},Initialization:>{f[x_] := x^2}]

そこに犯人がいます。Manipulateボックスの初期化部分はfを再度定義しますが、定義を変更した後に再評価しなかったため、古いバージョンです。操作ボックスが画面に表示されるとすぐに評価され、古い定義が元に戻ります。グローバルに!

もちろん、このおもちゃの例では、何か奇妙なことが起こっていることがすぐにわかります。私の場合、大きなノートブックに大きなモジュールがあり、デバッグ後に小さな部分を変更しました。それはうまくいったように見えましたが、翌日、再びヒットする前に私を悩ませていたのと同じバグがありました。手元にある問題をあらゆる側面から研究するために使用したいくつかのマニピュレートの1つがこれを行っていることに気付くまで、数時間かかりました。

明らかに、これは望ましくない動作であると言いたくなります。さて、必須の質問です。ノートブックで使用される可能性のある定義を変更するたびに、ノートブック内のすべてを再実行する以外に、この背後にある動作が発生しないようにするにはどうすればよいでしょうか。ManipulateManipulate

4

2 に答える 2

10

これが試みです。アイデアは、操作されたコード内のシンボルDownValuesまたはその他のシンボルを識別し、それらの代わりに一意の変数/シンボルを使用してシンボルの名前を自動的に変更することです。...Valuesここでのアイデアは、私が時々役立つと思うシンボル機能のクローン作成の助けを借りて、かなりエレガントに実行することができます。以下の関数cloneは、指定されたシンボルのクローンを作成し、同じグローバル定義を持つシンボルを生成します。

Clear[GlobalProperties];
GlobalProperties[] :=
  {OwnValues, DownValues, SubValues, UpValues, NValues, FormatValues, 
      Options, DefaultValues, Attributes};


Clear[unique];
unique[sym_] :=
 ToExpression[
    ToString[Unique[sym]] <> 
       StringReplace[StringJoin[ToString /@ Date[]], "." :> ""]];


Attributes[clone] = {HoldAll};
clone[s_Symbol, new_Symbol: Null] :=
  With[{clone = If[new === Null, unique[Unevaluated[s]], ClearAll[new]; new],
        sopts = Options[Unevaluated[s]]},
     With[{setProp = (#[clone] = (#[s] /. HoldPattern[s] :> clone)) &},
        Map[setProp, DeleteCases[GlobalProperties[], Options]];
        If[sopts =!= {}, Options[clone] = (sopts /. HoldPattern[s] :> clone)];
        HoldPattern[s] :> clone]]

関数自体を実装する方法にはいくつかの選択肢があります。Manipulate1つは、たとえばと同じ引数をとって、別の名前で関数を導入することmyManipulateです。もう1つ使用します。カスタムラッパーManipulateを介してソフトにオーバーロードしUpValuesます。これを紹介します。私はそれを呼びますCloneSymbols。コードは次のとおりです。

ClearAll[CloneSymbols];
CloneSymbols /: 
Manipulate[args___,CloneSymbols[sd:(SaveDefinitions->True)],after:OptionsPattern[]]:=
   Unevaluated[Manipulate[args, sd, after]] /.
     Cases[
       Hold[args],
       s_Symbol /; Flatten[{DownValues[s], SubValues[s], UpValues[s]}] =!= {} :> 
          clone[s],
       Infinity, Heads -> True];

使用例を次に示します。

f[x_] := Sin[x];
g[x_] := x^2;

新しい機能を使用するには、SaveDefinitions->TrueオプションをCloneSymbolsラッパーでラップする必要があることに注意してください。

Manipulate[Plot[ f[n g[x]], {x, -3, 3}], {n, 1, 4}, 
          CloneSymbols[SaveDefinitions -> True]]

操作する

これは、内部のコード内の元のシンボルの定義には影響しません。これは、Manipulate定義が保存され、初期化で使用されているクローンであるためです。FullFormこれを見て、次のManipulateことを確認できます。

Manipulate[Plot[f$37782011751740542578125[Times[n,g$37792011751740542587890[x]]],
   List[x,-3,3]],List[List[n,1.9849999999999999`],1,4],RuleDelayed[Initialization,
     List[SetDelayed[f$37782011751740542578125[Pattern[x,Blank[]]],Sin[x]],
       SetDelayed[g$37792011751740542587890[Pattern[x,Blank[]]],Power[x,2]]]]]

特に、関数の定義を変更して次のように言うことができます

f[x_]:=Cos[x];
g[x_]:=x;

次に、上記で作成したスライダーを動かしてManipulate、関数の定義を確認します

?f
Global`f
f[x_]:=Cos[x]

?g
Global`g
g[x_]:=x

これManipulateは合理的に何からも独立しており、安全にコピーして貼り付けることができます。ここで何が起こるかは次のとおりです。最初に、自明でないDownValuesSubValuesまたはUpValues(おそらく追加することもできます)を持つすべてのシンボルを見つけ、それらのクローンをその場OwnValuesで使用Casesして作成します。clone次に、複製されたすべての記号を内部のクローンに字句的に置き換えてから、クローンの定義を保存しますManipulateManipulateこのようにして、関連する関数の「スナップショット」を作成しますが、元の関数にはまったく影響しません。

クローン(シンボル)の一意性は、関数で対処されていuniqueます。ただし、Manipulateこの方法で取得された-sは元の関数定義を脅かすことはありませんが、通常はそれらに依存するため、完全に独立しているとは見なされないことに注意してください。Manipulateで完全にスタンドアロンの「スナップショット」を作成するには、依存関係ツリーをたどってそこにあるすべてのシンボルのクローンを作成し、それらの相互依存関係を再構築する必要があります。これは実行可能ですが、より複雑です。

編集

@Sjoerdのリクエストごとに、Manipulate-sを関数の変更に更新したいが、グローバル定義を積極的に干渉して変更したくない場合のコードを追加します。「ポインタ」手法の変形を提案します。関数名を新しいシンボルに置き換えますが、関数の後にこれらの新しいシンボルを複製するのではなく、Manipulate'sInitializationオプションを使用して、これらのシンボルを単純に「ポインタ」にします。たとえば、のような関数Initialization:>{new1:=f,new2:=g}。明らかに、そのような初期化コードの再評価は、fまたはの定義に害を及ぼすことはなくg、同時に、Manipulate-sはそれらの定義の変更に応答するようになります。

最初の考えは、関数名を新しいシンボルに置き換えるだけでManipulate、残りは初期化によって自動的に実行できるということです。残念ながら、そのプロセスでは、依存関係ツリーをたどるので、関数の定義も含まれます。これは、回避しようとしていることです。したがって、代わりに、オプションを明示的に構築しInitializeます。コードは次のとおりです。

ClearAll[SavePointers];
SavePointers /: 
Manipulate[args___,SavePointers[sd :(SaveDefinitions->True)],
after:OptionsPattern[]] :=
Module[{init},
  With[{ptrrules = 
    Cases[Hold[args], 
      s_Symbol /; Flatten[{DownValues[s], SubValues[s], UpValues[s]}] =!= {} :> 
         With[{pointer = unique[Unevaluated[s]]},
            pointer := s;
            HoldPattern[s] :> pointer], 
            Infinity, Heads -> True]},
           Hold[ptrrules] /. 
              (Verbatim[HoldPattern][lhs_] :> rhs_ ) :> (rhs := lhs) /. 
               Hold[defs_] :> 
                 ReleaseHold[
                      Hold[Manipulate[args, Initialization :> init, after]] /. 
                            ptrrules /. init :> defs]]]

以前と同じ定義で:

ClearAll[f, g];
f[x_] := Sin[x];
g[x_] := x^2;

これFullFormが生産されたものManipulateです:

In[454]:= 
FullForm[Manipulate[Plot[f[n g[x]],{x,-3,3}],{n,1,4},
     SavePointers[SaveDefinitions->True]]]

Out[454]//FullForm=   
Manipulate[Plot[f$3653201175165770507872[Times[n,g$3654201175165770608016[x]]],
List[x,-3,3]],List[n,1,4],RuleDelayed[Initialization,
List[SetDelayed[f$3653201175165770507872,f],SetDelayed[g$3654201175165770608016,g]]]]

新しく生成されたシンボルは、関数への「ポインター」として機能します。このManipulateアプローチで構築された-sは、関数の更新に応答すると同時に、主要な関数の定義に無害です。支払う代償は、それらが自己完結型ではなく、主な機能が定義されていない場合は正しく表示されないことです。したがって、必要なものに応じて、CloneSymbolsラッパーまたはのいずれかを使用できます。SavePointers

于 2011-07-05T09:08:52.163 に答える
6

Manipulate答えは、初期化セルを:の初期化として使用することです。

Manipulate[
 Plot[n f[x], {x, -3, 3}], {n, 1, 4}, 
 Initialization :> FrontEndTokenExecute["EvaluateInitialization"]]

また、使用することができますDynamicModule

DynamicModule[{f},
 f[x_] := x^2;
 Manipulate[Plot[n f[x], {x, -3, 3}], {n, 1, 4}]]

この場合は必要ありませんSaveDefinitions -> True

編集

Sjoerdのコメントに応えて。次の簡単な手法を使用すると、定義をどこにでもコピーして、定義を変更した場合にすべてのコピーを更新する必要はありません(ただし、更新するにはコードを再評価する必要がありますManipulate)。

DynamicModule[{f}, f[x_] := x^2;
  list = Manipulate[Plot[n^# f[x], {x, -3, 3}], {n, 2, 4}] & /@ Range[3]];
list // Row
于 2011-07-05T09:00:59.920 に答える