gpt4 book ai didi

wolfram-mathematica - 保存定义被认为是危险的

转载 作者:行者123 更新时间:2023-12-03 12:17:02 26 4
gpt4 key购买 nike

SaveDefinitionsManipulate的一个不错的选择。它使Manipulate在“操纵”面板中存储用于创建它的所有定义。这样制作的机械手可以复制到空笔记本上,并且仍然可以单独工作。此外,您的包含许多“操作”的工作笔记本也不会变成一堆粉红色的盒子,打开后它下面会显示错误消息。大!

但是,所有这些好处都有其黑暗的一面,如果您不了解,它可能会给您带来极大的痛苦。我已经在工作了几天的笔记本中拥有此功能,但是我向您展示了一个重现玩具问题的分步玩具示例场景。

在这种情况下,您想创建一个Manipulate来显示一个漂亮的波浪函数的图,因此您要定义它(请像这样设置窗口大小,这很重要):

该定义很好,因此我们下次将其保留,并使其成为初始化单元格。接下来,我们添加Manipulate,并执行它。

f[x_] := x^2

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

所有的作品都很棒,Manipulate确实发光,这是美好的一天。

只是偏执的自我,您要检查定义是否正确:

是的,一切仍然可以进行。精细。但是现在您想到,更好的波浪函数将是一个正弦波,因此您可以更改定义,执行并偏执,请检查:

一切还好。经过一天的辛苦工作,您已经做好准备,可以保存工作并退出。 [退出内核]

明天。您再次开始工作。您可以评估笔记本中的初始化单元。定义还好吗?检查一下

现在,您向下滚动到Manipulate框(无需使用 SaveDefinitions来重新执行),并使用滑块进行一些操作。并向上滚动。

作为偏执狂,您再次检查f的定义:

瞧,有人改变了您背后的定义!在第一个和第二个 Information(?)之间不会执行任何操作,根据In []数字检查( In[1]:f的def, In[2]第一个?, In[3]第二个)。

发生了什么?好吧,这当然是 ManipulateFullForm揭示了其内部结构:
Manipulate[Plot[n*f[x],{x, -3, 3}],{{n, 2.44}, 1, 4},Initialization:>{f[x_] := x^2}]

那里是罪魁祸首。框的初始化部分再次定义了f,但是它是旧版本,因为我们在修改 Manipulate的定义后没有重新评估它。一旦操作框出现在屏幕上,便会对其进行评估,并且您已经恢复了原来的定义。全局!

当然,在这个玩具示例中,很明显,正在发生奇怪的事情。就我而言,我在一个较大的笔记本电脑中有一个较大的模块,经过一些调试,我在其中做了很小的改动。它似乎有效,但是第二天,又一次被我困扰的错误再次被击中。我花了几个小时才意识到,我过去一直在研究来自各个方面的问题的几个操作中的一个正在执行此操作。

显然,我很想说这是不想要的行为。现在,对于强制性问题: ,除了每次您更改笔记本可能使用的定义时,重新执行笔记本中的每个Manipulate之外,我们还可以采取什么措施来防止Manipulate的这种背后行为?

最佳答案

这是一个尝试。这个想法是用操纵代码中的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。我将使用另一个:我将介绍的一些自定义包装器的 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内代码中原始符号的定义,因为它们是其副本的副本,其定义已保存并现在用于初始化。我们可以查看此 FullFormManipulate来确认:
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合理地独立于任何东西,可以安全地复制和粘贴。这里发生的事情如下:我们首先找到所有带有非平凡 DownValuesSubValuesUpValues(也可以添加 OwnValues)的符号,然后使用 Casesclone即时创建其克隆。然后,将所有克隆的符号用词法替换为 Manipulate内的克隆,然后让 Manipulate保存克隆的定义。这样,我们就可以对所涉及的功能进行“快照”,但不会以任何方式影响原始功能。

克隆(符号)的唯一性已通过 unique函数解决。但是请注意,尽管以这种方式获得的 Manipulate -s不会威胁到原始函数定义,但是它们通常仍将依赖于它们,因此人们不能认为它们完全独立于任何事物。人们将不得不走下依赖树并在其中克隆所有符号,然后重建它们之间的依赖关系,以在Manipulate中构造一个完全独立的“快照”。这是可行的,但更为复杂。

编辑

对于@Sjoerd的请求,我添加了一种代码,用于当我们确实希望我们的 Manipulate -s更新到函数的更改,但又不希望它们主动干预并更改任何全局定义时。我建议使用“指针”技术的一种变体:我们将再次用新的符号替换函数名称,但是,我们将使用 ManipulateInitialization选项简单地使这些符号成为“指针”,而不是在函数之后克隆这些新符号。到我们的函数,例如 Initialization:>{new1:=f,new2:=g}。显然,重新评估此类初始化代码不会损害 fg的定义,与此同时,我们的 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;

这是产生的 FullFormManipulate:
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

关于wolfram-mathematica - 保存定义被认为是危险的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6579644/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com