关于在函数之间传递数据的简单问题

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)

只返回未评估形式的最后一个语句.

  • +1.严格地说,"Unevaluated"(以及"Sequence"或"Evaluate"之类的东西)不是真正的函数.相反,它们是影响主要评估序列的特殊头(形式).特别是,他们AFAIK没有内置规则,你会发现很难用一些用户定义的代码完全复制他们的行为 - 它们比其他内置函数更深入. (2认同)

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)

编辑

只是为了完整性,这是另一种方式,它不太优雅,但我发现自己不时使用,特别是当你有几个参数并想要持有多个(但是这样HoldFirstHoldRest不够好,如例如,第一个和第三个):只需将参数包装起来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线程.