示例:
list:={ Plus[1,1], Times[2,3] }
在查看list
时,我得到了
{2,6}
我想让他们不受评估(如上所述),以便list
返回
{ Plus[1,1], Times[2,3] }
后来我想评估列表序列中的函数来获取
{2,6}
事先不知道list
中未评估的函数数量。除Plus
外,f[x_]
等用户定义的函数可以存储在list
我希望这个例子很明确。
这样做的最佳方式是什么?
答案 0 :(得分:10)
最好的方法是将它们存储在Hold
中,而不是List
中,如下所示:
In[255]:= f[x_] := x^2;
lh = Hold[Plus[1, 1], Times[2, 3], f[2]]
Out[256]= Hold[1 + 1, 2 3, f[2]]
通过这种方式,您可以完全控制它们。在某些时候,您可以致电ReleaseHold
来评估它们:
In[258]:= ReleaseHold@lh
Out[258]= Sequence[2, 6, 4]
如果您希望将结果放在列表中而不是Sequence
,则可以仅使用List@@lh
。如果您需要评估特定的一个,只需使用Part
来提取它:
In[261]:= lh[[2]]
Out[261]= 6
如果你坚持你的建筑,这是一种方式:
In[263]:= l:={Plus[1,1],Times[2,3],f[2]};
Hold[l]/.OwnValues[l]
Out[264]= Hold[{1+1,2 3,f[2]}]
修改强>
如果您的某些功能/符号UpValues
甚至可以在Hold
内进行评估,您可能需要使用HoldComplete
代替Hold
。
<强> EDIT2 强>
正如@W.Wizard在另一个答案中指出的那样,有时您可能会发现让Hold
包裹在序列中的单个项目中会更方便。我在这里的评论是,一旦我们意识到将这一种形式转换为另一种形式并将其转化为非常容易,两种形式的有用性就会被放大。以下函数会将Hold
内的序列拆分为保留项目列表:
splitHeldSequence[Hold[seq___], f_: Hold] := List @@ Map[f, Hold[seq]]
例如,
In[274]:= splitHeldSequence[Hold[1 + 1, 2 + 2]]
Out[274]= {Hold[1 + 1], Hold[2 + 2]}
将它们分组回一个Hold
更加容易 - 只需Apply
Join
:
In[275]:= Join @@ {Hold[1 + 1], Hold[2 + 2]}
Out[275]= Hold[1 + 1, 2 + 2]
两种不同的形式在不同的情况下是有用的。您可以在保留项目列表中轻松使用Union
,Select
,Cases
等内容,而无需考虑评估。完成后,您可以将它们组合回单个Hold
,例如,将某些函数作为未评估的参数序列提供。
编辑3
根据@ ndroock1的请求,这是一个具体的例子。设置:
l = {1, 1, 1, 2, 4, 8, 3, 9, 27}
S[n_] := Module[{}, l[[n]] = l[[n]] + 1; l]
Z[n_] := Module[{}, l[[n]] = 0; l]
将功能放在Hold
:
In[43]:= held = Hold[Z[1], S[1]]
Out[43]= Hold[Z[1], S[1]]
以下是exec
函数的外观:
exec[n_] := MapAt[Evaluate, held, n]
现在,
In[46]:= {exec[1], exec[2]}
Out[46]= {Hold[{0, 1, 1, 2, 4, 8, 3, 9, 27}, S[1]], Hold[Z[1], {1, 1, 1, 2, 4, 8, 3, 9, 27}]}
请注意,原始变量held
保持不变,因为我们对副本进行操作。另请注意,原始设置包含可变状态(l
),这在Mathematica中不是很惯用。特别是,评估的顺序很重要:
In[61]:= Reverse[{exec[2], exec[1]}]
Out[61]= {Hold[{0, 1, 1, 2, 4, 8, 3, 9, 27}, S[1]], Hold[Z[1], {2, 1, 1, 2, 4, 8, 3, 9, 27}]}
是否需要这取决于具体需求,我只想指出这一点。此外,虽然上面的exec
是根据请求的规范实现的,但它隐含地取决于全局变量l
,我认为它是bad practice。
存储由@ Mr.Wizard建议的函数的另一种方法可以实现,例如,像
在[63]中:= listOfHeld = splitHeldSequence [hold]
在这里
In[64]:= execAlt[n_] := MapAt[ReleaseHold, listOfHeld, n]
In[70]:= l = {1, 1, 1, 2, 4, 8, 3, 9, 27} ;
{execAlt[1], execAlt[2]}
Out[71]= {{{0, 1, 1, 2, 4, 8, 3, 9, 27}, Hold[S[1]]}, {Hold[Z[1]], {1, 1, 1, 2, 4, 8, 3, 9, 27}}}
关于可变性和对全局变量的依赖性的相同评论也在这里。最后一种形式也更适合查询函数类型:
getType[n_, lh_] := lh[[n]] /. {Hold[_Z] :> zType, Hold[_S] :> sType, _ :> unknownType}
例如:
In[172]:= getType[#, listOfHeld] & /@ {1, 2}
Out[172]= {zType, sType}
答案 1 :(得分:7)
首先想到的是不使用List
而是使用这样的东西:
SetAttributes[lst, HoldAll];
heldL=lst[Plus[1, 1], Times[2, 3]]
但肯定会有更多博学的建议!
答案 2 :(得分:5)
您还可以对要保留的每个元素使用Hold
:
a = {Hold[2 + 2], Hold[2*3]}
您可以在元素或列表中使用HoldForm
,如果您希望列表的外观不可见Hold
:
b = {HoldForm[2 + 2], HoldForm[2*3]}
c = HoldForm@{2 + 2, 2*3}
{2 + 2, 2 * 3}
您可以使用ReleaseHold
恢复评估表单:
a // ReleaseHold
b // ReleaseHold
c // ReleaseHold
Out[8]= {4, 6}
Out[9]= {4, 6}
Out[10]= {4, 6}
表单Hold[2+2, 2*3]
或上面的a
和b
表单很好,因为您可以轻松地添加以下内容: Append
。对于b
类型,逻辑上是:
Append[b, HoldForm[8/4]]
Hold[2+2, 2*3]
:
Hold[2+2, 2*3] ~Join~ Hold[8/4]
答案 3 :(得分:3)
另一种方式:
lh = Function[u, Hold@u, {HoldAll, Listable}];
k = lh@{2 + 2, Sin[Pi]}
(*
->{Hold[2 + 2], Hold[Sin[\[Pi]]]}
*)
ReleaseHold@First@k
(*
-> 4
*)