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

时间:2011-09-11 02:53:06

标签: wolfram-mathematica wolfram-cdf

简单的问题,但我要求确保我不会忽视一个明显的问题 解决方案可以更有效率。

如果有一个大数据缓冲区,比如非常大的列表,需要更新,并且 想把它传递给函数来在函数内部进行更新,如

a = Table[0,{10}]
a = update[a]

因为我不能使用传递引用(在CDF中,不能改变 函数Atrributes到任何东西,比如HoldFirst),然后我被迫 在函数本身内复制一个列表以便更新它, 并返回副本。

我的问题,除了使用不好的“全局变量”之外, 有没有更有效的方法来做到这一点?

PS。大约一年前,我询问了参考资料,here is a link 我的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}

如果我可以使用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}

更高效,因为没有复制。通过引用传递。

我可以使用全局变量,如

a = Table[0, {10}];
updateMya[] := Module[{}, a[[1]] = 5]
updateMya[];
a
----> {5, 0, 0, 0, 0, 0, 0, 0, 0, 0}

但即使它非常快,这当然也是错误的编程。

由于我有大量数据缓冲区,而且我想模块化我的Mathematica代码, 我需要创建函数,我将大数据传递给进程,但同时 时间想要保持“高效”。

人们可以看到其他任何选项吗?

抱歉,如果之前有人问过这个问题,很难搜索到。

感谢,

加1

使用Unevaluated易于使用,但我不再能够使用类型检查来确保传递列表。例如

update[a_List] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

现在调用不会“绑定”到定义,因为'a'现在没有标题列表。

所以,我失去了代码中的一些强大功能。但是使用Unevaluated可以在CDF中工作并且更改代码以使用它很容易。我只是删除那些额外的'类型检查',我在那里使它工作。

2 个答案:

答案 0 :(得分:16)

函数Unevaluated与(暂时)设置属性HoldFirst具有非常相同的效果,因此您可以执行类似

的操作
update[a_] := Module[{}, a[[1]] = 5; a]
a = Table[0, {10}];
a = update[Unevaluated[a]]

修改

关于添加1:您可以通过执行类似

的操作来添加类型检查
Clear[update];
update[a_] := Module[{}, a[[1]] = 5; a] /; Head[a] == List

然后

a = Table[0, {10}];
update[Unevaluated[a]]

像以前一样工作但是

b = f[1,2,3];
update[Unevaluated[b]]

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

答案 1 :(得分:12)

或者,如果CDF允许,你可以使用带有Hold * -attribute的纯函数,如下所示:

update = Function[a, a[[1]] = 5; a, HoldFirst]

然后,你像往常一样使用它:

In[1408]:= 
a=Table[0,{10}];
update[a];
a

Out[1410]= {5,0,0,0,0,0,0,0,0,0}

修改

为了完整起见,这是另一种方式,它不那么优雅,但我发现自己不时使用,特别是当你有几个参数并希望持有多个时(但HoldFirstHoldRest不够好,例如第一个和第三个):只需将参数包装在Hold中,并将其记录在函数的签名中,如下所示:

updateHeld[Hold[sym_], value_] := (sym[[1]] = value; sym)

您将其用作:

In[1420]:= a=Table[0,{10}];
updateHeld[Hold[a],10];
a

Out[1422]= {10,0,0,0,0,0,0,0,0,0}

编辑2

如果你主要关注的是封装,你也可以使用Module创建持久的局部变量和方法来访问和修改它,如下所示:

Module[{a},
   updateA[partIndices__, value_] := a[[partIndices]] = value;
   setA[value_] := a = value;
   getA[] := a
]

从结构的角度来看,它仍然是(alomost)一个全局变量,但是没有与其他变量名称冲突的危险,并且更容易跟踪它的更改位置,因为你只能通过使用它来实现它上面的mutator方法(但不是直接的)。你用它作为:

In[1444]:= 
setA[Table[0,{10}]];
updateA[1,5];
getA[]

Out[1446]= {5,0,0,0,0,0,0,0,0,0}

这就像在Java中创建一个简单的JavaBean - 一个可变数据的容器(一种封装状态的方法)。由于额外的方法调用(基于保持属性或基于Unevaluated的方法),您将有轻微的开销,并且在许多情况下您不需要它,但在某些情况下,您可能希望封装状态,如 - 它可以使您的(有状态)代码更容易测试。就个人而言,我已经为UI编程和与数据库接口相关的代码做了几次。

本着同样的精神,您还可以在函数之间共享一些变量,在Module范围内定义这些函数 - 在这种情况下,您可能不需要getter和setter方法,并且具有共享状态的此类全局函数是closures。您可以在this MathGroup主题的第三篇文章中找到更详细的讨论。