这是一个尝试。这个想法是用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 相同的参数,例如myManipulate。我将使用另一个:通过UpValues 软重载Manipulate 一些自定义包装器,我将介绍它。我会称之为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 的Initialization 选项来简单地制作这些符号,而不是在我们的函数之后克隆这些新符号指向我们函数的符号“指针”,例如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。