Nas*_*ser 9 wolfram-mathematica wolfram-cdf
简单的问题,但我要求确保我不会忽视一个更有效的明显解决方案.
如果有一个大数据缓冲区,比如非常大的列表,需要更新,并希望将其传递给函数以在函数内部进行更新,如
a = Table[0,{10}]
a = update[a]
Run Code Online (Sandbox Code Playgroud)
并且因为我不能使用pass by reference(在CDF中,不能将函数的Atrributes更改为任何东西,例如HoldFirst),然后我被迫在函数内部按顺序复制列表更新它,并返回副本.
我的问题,除了使用不好的"全局变量"之外,还有更有效的方法吗?
PS.大约一年前,我通过引用询问了复制,这里是 我的Mathgroup问题的链接.(感谢Leonid回答btw,是有用的答案).
但我的问题在这里有点不同,因为现在我不能使用HoldFirst,还有其他任何我没有看到的替代方案,以避免这种额外的数据复制,它似乎会在程序变大时减慢程序的速度大.
(不能使用SetAttributes及其朋友,不允许使用CDF).
我将首先展示基本示例,然后展示如果我可以使用HoldFirst,我将如何做.
update[a_List] := Module[{copyOfa = a}, copyOfa[[1]] = 5; copyOfa]
a = Table[0, {10}];
a = update[a]
----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Run Code Online (Sandbox Code Playgroud)
如果我可以使用HoldFirst,我会写
update[a_] := Module[{}, a[[1]] = 5; a]
Attributes[update] = {HoldFirst};
a = Table[0, {10}];
a = update[a]
----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Run Code Online (Sandbox Code Playgroud)
效率更高,因为没有复制.通过引用传递.
我可以使用全局变量,如
a = Table[0, {10}];
updateMya[] := Module[{}, a[[1]] = 5]
updateMya[];
a
----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Run Code Online (Sandbox Code Playgroud)
但即使它非常快,这当然也是错误的编程.
由于我有大量数据缓冲区,并且我想模块化我的Mathematica代码,我需要创建函数,我将大数据传递给进程,但同时又希望保持"高效".
人们可以看到任何其他选项吗?
对不起,如果之前有人问过这个问题,很难搜索到.
谢谢,
另外1
使用Unevaluated很容易使用,但我不再能够使用类型检查来确保传递列表.例如
update[a_List] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]
Run Code Online (Sandbox Code Playgroud)
该调用现在不会"绑定"到定义,因为'a'现在没有标题List.
所以,我失去了代码中的一些强大功能.但是使用Unevaluated可以在CDF中工作并且更改代码以使用它很容易.我只是删除那些额外的'类型检查',我在那里使它工作.
Hei*_*ike 16
该函数Unevaluated与(临时)设置属性具有非常相同的效果,HoldFirst因此您可以执行类似的操作
update[a_] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]
Run Code Online (Sandbox Code Playgroud)
编辑
关于加法1:你可以通过做类似的事情来添加类型检查
Clear[update];
update[a_] := Module[{}, a[[1]] = 5; a] /; Head[a] == List
Run Code Online (Sandbox Code Playgroud)
然后
a = Table[0, {10}];
update[Unevaluated[a]]
Run Code Online (Sandbox Code Playgroud)
像以前一样工作但是
b = f[1,2,3];
update[Unevaluated[b]]
Run Code Online (Sandbox Code Playgroud)
只返回未评估形式的最后一个语句.
Leo*_*rin 12
或者,如果CDF允许,您可以使用带有Hold*-attribute的纯函数,如下所示:
update = Function[a, a[[1]] = 5; a, HoldFirst]
Run Code Online (Sandbox Code Playgroud)
然后,你像往常一样使用它:
In[1408]:=
a=Table[0,{10}];
update[a];
a
Out[1410]= {5,0,0,0,0,0,0,0,0,0}
Run Code Online (Sandbox Code Playgroud)
编辑
只是为了完整性,这是另一种方式,它不太优雅,但我发现自己不时使用,特别是当你有几个参数并想要持有多个(但是这样HoldFirst或HoldRest不够好,如例如,第一个和第三个):只需将参数包装起来Hold,并将其记录在函数的签名中,如下所示:
updateHeld[Hold[sym_], value_] := (sym[[1]] = value; sym)
Run Code Online (Sandbox Code Playgroud)
你用它作为:
In[1420]:= a=Table[0,{10}];
updateHeld[Hold[a],10];
a
Out[1422]= {10,0,0,0,0,0,0,0,0,0}
Run Code Online (Sandbox Code Playgroud)
编辑2
如果您主要关心的是封装,您还可以使用Module创建持久局部变量和方法来访问和修改它,如下所示:
Module[{a},
updateA[partIndices__, value_] := a[[partIndices]] = value;
setA[value_] := a = value;
getA[] := a
]
Run Code Online (Sandbox Code Playgroud)
从结构的角度来看,它仍然是(alomost)一个全局变量,但是没有与其他变量名称冲突的危险,并且更容易跟踪它的变化,因为你只能通过使用mutator方法来实现它.以上(但不是直接).你用它作为:
In[1444]:=
setA[Table[0,{10}]];
updateA[1,5];
getA[]
Out[1446]= {5,0,0,0,0,0,0,0,0,0}
Run Code Online (Sandbox Code Playgroud)
这就像在Java中创建一个简单的JavaBean - 一个可变数据的容器(一种封装状态的方法).由于额外的方法调用(wrt保持属性或Unevaluated基于方法),您将有轻微的开销,并且在许多情况下您不需要它,但在某些情况下,您可能希望封装状态 - 它可能会您的(有状态)代码更容易测试.就个人而言,我已经为UI编程和与数据库接口相关的代码做了几次.
本着同样的精神,您还可以在函数之间共享一些变量,在作用Module域内定义这些函数- 在这种情况下,您可能不需要getter和setter方法,并且这样的全局函数具有共享状态closures.你可以在我的第三个岗位中找到这个更详细的讨论这个 MathGroup线程.
| 归档时间: |
|
| 查看次数: |
1424 次 |
| 最近记录: |