これが試みです。アイデアは、操作されたコード内のシンボル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]]
関数自体を実装する方法にはいくつかの選択肢があります。Manipulate
1つは、たとえばと同じ引数をとって、別の名前で関数を導入すること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
は合理的に何からも独立しており、安全にコピーして貼り付けることができます。ここで何が起こるかは次のとおりです。最初に、自明でないDownValues
、SubValues
またはUpValues
(おそらく追加することもできます)を持つすべてのシンボルを見つけ、それらのクローンをその場OwnValues
で使用Cases
して作成します。clone
次に、複製されたすべての記号を内部のクローンに字句的に置き換えてから、クローンの定義を保存しますManipulate
。Manipulate
このようにして、関連する関数の「スナップショット」を作成しますが、元の関数にはまったく影響しません。
クローン(シンボル)の一意性は、関数で対処されてい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