在C语言中,如果我想在调用之间保存局部变量值,则在函数内定义局部变量时,通常在变量旁边添加“静态”一词。
这给了我变量名称的本地作用域保护,但是同时,变量值是持久的。模拟一个保存其自己的私有状态的简单对象非常有用。
http://en.wikipedia.org/wiki/Local_variable#Static_local_variables
可以使用Module[]
在Mathematica中对此进行仿真吗?还是DynamicModule[]
?
(我们知道Module[]
默认情况下不会保存局部变量状态,但是使用DynamicModule[]
怎么办?)
不使用软件包。使用它的上下文在Manipulate[]
内部。Module[]
不能在Manipulate之外。因此,一切都必须是这样的:
Manipulate[
foo[]:=Module[{a,b,c},....];
boo[]:=Module[{a,b,c},....];
... foo[] ..
... boo[]...
..,
control_variables...,
Initialization:>
(
.... global area....
)
]
我尝试使用
DynamicModule
代替上面的Module[]
来执行此操作,但是DynamicModules
不能被称为函数吗?我不知道该怎么做。问题是:是否可以使函数记住两次调用之间的局部变量值,例如可以将C与局部静态变量一起使用?
当然,不能将状态保存在全局变量中,这是我提出这一问题的全部要点。我想将模块状态保存在仅属于该模块的上下文中。
我不要求任何高级OO仿真。只是想在名称范围内保存一个模块使用的局部变量,而不是在全局上下文中属于该模块,并在调用模块之间保持它们不变。
更新资料
要明确的是,这是一个非常基本的C示例
#include <stdio.h>
void my_add(void)
{
static int total = 0;
total = total + 1;
printf("current total %d\n",total);
}
int main()
{
int i;
for(i = 1; i<=3; i++)
my_add();
return 0;
}
$ gcc ex.c
$ ./a.exe
current total 1
current total 2
current total 3
$
更新8:45 AM
这是WReach解决方案。这是我尝试过的:
Manipulate[
n;
Module[{total = 0},
processA[] :=
(
total = total + 1
)
];
Module[{total = 0},
processB[] :=
(
total = total + 1
)
];
Grid[{
{"module A total=", processA[]},
{"module B total=", processB[]}
}],
Button["press to update state", n++],
{{n, 0}, None},
TrackedSymbols :> {n}
]
每次单击按钮时,总数仍为1。它不保存最后的值。
更新9:13 AM
对于Manipulate内部的WReach解决方案:
这是一个测试:
Manipulate[
n;
Grid[{
{"module A result=", aResult}
}],
Button["press to update process A", {n++; aResult = processA[n]}],
{{n, 0}, None},
{{aResult, {}}, None},
TrackedSymbols :> {n},
Initialization :>
(
Module[{total = 0},
processA[n_] :=
(
total = total + 1;
{n, total}
)
]
)
]
就其本身而言,它似乎有效。但是当我复制单元格并将其粘贴到新单元格时。然后运行第二个Manipulate,以在那里更新总数,然后回到第一个Manipulate来更新那里的总数,我看到它正在使用第二个Manipulate中更新的总数。因此,它是全球性的。
演示中不允许这样做。拍摄快照,它们不能共享状态(演示中不允许使用全局变量。初始化部分中不得包含全局共享数据。但是可以包含仅通过Manipulate表达式通过参数调用的函数,没问题) 。
更新9:40 AM
对于他显示的模式,请回答以下向导先生的第二个示例,如下所示:
Manipulate[
{x, myAdd[]},
{x, 1, 10},
{{total, 0}, None},
{{myAdd, (total += 1; total) &}, None}
]
问题在于,名称
total
不能被2个不同的函数使用。 total
的名称空间遍及整个Manipulate。我想要的是,使用这种模式是这样的:Manipulate[
ctrl;
Grid[{
{"processA result=", processA[]},
{"processB result=", processB[]}
}],
Button["step", ctrl++],
{{ctrl, 0}, None},
{{processA, Module[{total = 0}, total++] &}, None},
{{processB, Module[{total = 0}, total++] &}, None},
TrackedSymbols :> {ctrl}
]
您可以在上面看到processA有自己的本地总数,processB也有自己的局部总数。相同的本地名称。上面的方法不起作用。如果我将以下内容替换为“正常”
Manipulate[
ctrl;
Grid[{
{"processA result=", processA[]},
{"processB result=", processB[]}
}],
Button["step", ctrl++],
{{ctrl, 0}, None},
{{total, 0}, None},
{{processA, Module[{}, total++] &}, None},
{{processB, Module[{}, total++] &}, None},
TrackedSymbols :> {ctrl}
]
然后,processA和processB现在共享相同的总变量。这样就消除了使每个函数都位于单独的名称空间中并在调用调用的整个生命周期中保持持久性这一点。
上午10点更新
抱歉,在上面的示例中,我写该Module []的方法很糟糕。这就是为什么它不起作用。请忽略我上午9:40的更新。我现在正在纠正它,并将在几分钟后更新。它实际上可能正在工作。
更新10:08 AM
好的,这是独家新闻:在我9:40 AM时,我说“以上内容不起作用”,是因为我的设置错误,()的位置错误。我纠正了这个。现在,我将显示WReach在标题“避免全局定义”下引用的解决方案
消息是它适用于一个操纵。将操作复制到另一个单元后,计数器将在这两个操作之间共享。
这是一个示例:(我上面上午9:40的内容的更正版本)
Manipulate[
ctrl;
Print["in top of Manipulate"];
Grid[{
{"processA current total=", aResult},
{"processA current total=", bResult}
}],
Button["update A process", {ctrl++; aResult = processA[]}],
Button["update B process", {ctrl++; bResult = processB[]}],
{{ctrl, 0}, None},
{{aResult, 0}, None},
{{bResult, 0}, None},
{{processA, Module[{total = 0}, ( total = total + 1; total) &]},
None},
{{processB, Module[{total = 0}, (total = total + 1; total) &]}, None},
TrackedSymbols :> {ctrl}
]
只要有一份“操纵”副本,该设置即可生效。一旦将“操纵”自身复制到新单元格并进行修改,第一个将被更新。计数器是全局的:
因此,太可惜了,这些解决方案对我不起作用。稍后将尝试功能上下文的东西。但是需要首先了解更多信息。
更新12 PM
为了说明我现在所做的事情,在下面的回复中,对于我拥有的每个“求解器”,我都用
pn
前缀其每个参数的名称,其中n
是求解器ID号。所以我有p1StepNumber,p2StepNumber,p3StepNumber,p1Solution,p2Solution,p3Solution等...然后,当我要调用求解器1时,我将其传递给p1 *参数,并在返回时返回解决方案和所有更新,并保存在“操纵控制”->“无”区域中,以供以后调用,依此类推。
因此,每个求解器的状态在“控制”中保留/保存为Control-> None变量。由于Manipulate是DynamicModule,因此它们将保存在调用之间,甚至当我关闭M并重新打开它时也是如此。
这是在“无控制”区域中部分列出我的“操纵”参数的屏幕快照,以进行说明。所有这些以及更多内容都在一个操纵中。当用户更改求解器时,整个UI也会随之更改,并具有特定于该求解器的新布局。
由于使用了Leonid Macro方法,我现在可以很容易地做到这一点:)。
如果我可以将这些“保存”在每个求解器的内部,并使每个求解器成为一个单独的模块,则更好,在该模块中,每个求解器将保存自己的状态,而Manipulate每次需要刷新时都将其传递给UI参数,并且其他所有内容都保存在它们所属的求解器中。
甚至没有一个结构来管理它,这意味着我进行的每个调用都具有20-30个以上的参数。由于我每次都需要传递求解器的所有状态,并随其返回一起返回。还算不错,我只需要习惯于每次使用30个参数进行调用,这是我不常用的东西。但是到目前为止,我还没有其他清洁的方法。这就是为什么我问这个问题。
更新11年12月24日下午6点
这是对Szabolcs和telefunkenvf14关于使用函数上下文名称的建议的回应。
我发现的结论是该方法也不起作用。与其他方法相同的问题。
一旦复制了“操作”本身,变量就会在“操作”的2个副本之间共享。
下面是显示问题的示例:
Remove["Global`*"]
Manipulate[
ctrl;
Print["in top of Manipulate"];
Grid[{
{"processA current total=", aResult},
{"processA current total=", bResult}
}],
Button["update A process", {ctrl++; aResult = processA[]}],
Button["update B process", {ctrl++; bResult = processB[]}],
{{ctrl, 0}, None},
{{aResult, 0}, None},
{{bResult, 0}, None},
{{processA, Module[{},
(
If[! ValueQ[processA`total], processA`total = 0];
processA`total = processA`total + 1;
processA`total
) &]}, None},
{{processB, Module[{},
(
If[! ValueQ[processB`total], processB`total = 0];
processB`total = processB`total + 1;
processB`total
) &]}, None},
TrackedSymbols :> {ctrl}
]
现在,将上面的内容复制到一个新的单元格中,然后单击按钮,然后看到计数器从上一个Manipulate副本中的值开始前进。因此,进程A是共享的。它对于每个特定功能都是本地的。
与尝试的其他方法完全相同的问题,因此该方法对我不起作用。
最佳答案
试试这个:
Module[{total = 0}
, myAdd[] := (total += 1; Print["current total ", total])
]
用:
In[2]:= myAdd[]
current total 1
In[3]:= myAdd[]
current total 2
In[4]:= myAdd[]
current total 3
出于实际目的,模块变量
total
仅对myAdd
可见。 Module
通过创建一个新的唯一符号来实现此目的,该符号的名称由提供的符号生成(本例中为total
)。您可以通过评估以下表达式来查看此信息:In[5]:= Module[{total}, total]
Out[5]= total$562
我们实际上可以访问
myAdd
使用的符号:In[6]:= Names["total*"]
Out[6]= {"total", "total$557", "total$562"}
In[7]:= total$557
Out[7]= 3
通过检查
myAdd
的定义,我们还可以看到重命名的效果:In[8]:= ??myAdd
Global`myAdd
myAdd[]:=(total$557+=1; Print[current total, total$557])
使用内部操纵
我们可以在
Manipulate
内部使用此技术,因此:Manipulate[
{x, myAdd[]}
, {x, 1, 10}
, Initialization :>
Module[{total = 0}
, myAdd[] := (total += 1; total)
]
]
避免全局定义
如果我们迫切希望避免使用
myAdd
的全局定义,则可以执行以下操作:Manipulate[
{x, myAdd[]}
, {x, 1, 10}
, { myAdd
, Module[{total = 0}, (total += 1; total) &]
, None
}
]
这样,
myAdd
的定义位于Manipulate
中。但是请注意,符号myAdd
和x
和total
仍然是全局符号。这是因为整个Manipulate
表达式都被读入全局上下文。如果这不能令人满意,那么我们将不得不将整个表达式放在另一个上下文中-无法回避符号必须存在于某些上下文中的事实。再一次突破
好的,这是解决增加的要求的另一种尝试:
Manipulate[
Module[{f, g}
, Module[{total = 0}, f[] := ++total]
; Module[{total = 0}, g[] := --total]
; Dynamic[{x, f[], g[]}]
]
, {x, 1, 10}
]
此解决方案具有以下功能:
f
和g
的定义位于单元格中total
的两次出现是相互隔离的,并且位于单元格中每当您操作
total
时,x
变量都不会重置为零如果您复制结果输出单元格,则该副本不会与第一个单元格共享任何状态