在Mathematica中将未求值的函数存储在列表中

10

示例:

list:={ Plus[1,1], Times[2,3] }

查看list时,我得到:

{2,6}

我希望保持它们不被评估(如上所述),以便list返回

{ Plus[1,1], Times[2,3] }

后来我想按列表顺序评估这些函数,以获得

{2,6}
list 中未计算函数的数量事先是未知的。除了 Plus 外,用户定义的函数如 f[x_] 可以存储在 list 中。
希望示例清晰明了。
最好的方法是什么?
4个回答

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

编辑2

正如@Mr.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甚至更容易——只需应用Join

In[275]:= Join @@ {Hold[1 + 1], Hold[2 + 2]}

Out[275]= Hold[1 + 1, 2 + 2]

两种不同的形式在不同的情况下都很有用。您可以轻松地在保留项目列表的情况下使用诸如UnionSelectCases等内容,而不必过多考虑评估。完成后,您可以将它们合并成单个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,我认为这是一个不好的实践
@Mr.Wizard建议的存储函数的另一种方法可以通过以下方式实现,例如:
In[63]:= listOfHeld = splitHeldSequence[held]
Out[63]= {Hold[Z1], Hold[S1]}
在这里
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}

这段程序的思路是: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]然后我想将{Z[1],S[1]}以未求值的形式存储在列表中。一个函数exec[n_]:='执行列表中位置n处的函数'。但我仍然无法使用Hold使其正常工作。 - nilo de roock
@ndroock1 - 请看我的编辑。如果可能的话,我会重新考虑设计,从不可变列表构建。 - Leonid Shifrin
编辑3可以实现。非常感谢。 (我将使用它来模拟Cutland的URM(无限寄存器机),这是类似于图灵机的概念。) - nilo de roock
@Leonid。还有一件事,存储和稍后评估的原因是我想要随机排列指令,并且需要跳转指令。这意味着我想要在Held中的位置上评估函数类型,它是Z、S还是其他?-如果您建议我这样做,我可以提出一个新问题。 - nilo de roock
@ndroock1 对于这个案例来说很容易,看看我的编辑。在这里有一些潜在的更一般的问题,比如:“如何根据它们的头部确定保留表达式的类型而不评估它们”,但你必须定义什么是类型等等。顺便说一句,尽量避免以大写字母开头的函数,这可能会与某些系统符号发生冲突。 - Leonid Shifrin

7
第一反应是不要使用 List ,而是使用类似于下面这样的东西:
 SetAttributes[lst, HoldAll];
 heldL=lst[Plus[1, 1], Times[2, 3]]

肯定会有更加博学的建议!

5
您可以在想要保留的每个元素上使用Hold:
a = {Hold[2 + 2], Hold[2*3]}

如果您想要列表的外观而不显示 Hold,则可以在元素或列表上使用 HoldForm

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]或者像上面的ab一样是很好的,因为你可以轻松地添加项,例如使用Append。对于b类型,它在逻辑上是:

Append[b, HoldForm[8/4]]

For Hold[2+2, 2*3]:

Hold[2+2, 2*3] ~Join~ Hold[8/4]

3
另一种方式:
lh = Function[u, Hold@u, {HoldAll, Listable}];
k = lh@{2 + 2, Sin[Pi]}
(*
->{Hold[2 + 2], Hold[Sin[\[Pi]]]}
*)
ReleaseHold@First@k
(*
-> 4
*)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接