简单的问题,但我要求确保我不会忽视一个明显的问题 解决方案可以更有效率。
如果有一个大数据缓冲区,比如非常大的列表,需要更新,并且 想把它传递给函数来在函数内部进行更新,如
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中工作并且更改代码以使用它很容易。我只是删除那些额外的'类型检查',我在那里使它工作。
答案 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}
修改强>
为了完整起见,这是另一种方式,它不那么优雅,但我发现自己不时使用,特别是当你有几个参数并希望持有多个时(但HoldFirst
或HoldRest
不够好,例如第一个和第三个):只需将参数包装在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主题的第三篇文章中找到更详细的讨论。