在Mathematica中的Hold中评估超出一个级别

时间:2010-06-28 22:28:27

标签: wolfram-mathematica

有关可能问题的评估的mathematica文档说:

  

评估仅适用于第一个   等级,直接在保持的函数内

为什么Mathematica有此限制?因此,如果我有一个包含多个级别的表达式,请使用此简化示例:

按住[加[加[2,2],2]]]

现在假设我想看看第二个Plus的答案是什么,而不评估它下面的任何级别。我尝试了不同的东西,例如:

In[290]:= Hold[Plus[Evaluate[Plus[2, 2]], 2]]
Out[290]= Hold[Evaluate[2+2]+2]

In[287]:= Hold[Plus[ReleaseHold[Hold[Plus[2, 2]]], 2]]
Out[287]= Hold[ReleaseHold[Hold[2+2]]+2]

在这种情况下,第一个Hold会将所有内容保持在第一级以及未超出的水平。 目标是使用连续的Hold,ReleaseHold和Evaluate函数来控制从最内部嵌套函数到外部函数的每个阶段的表达式的评估,以实现该目标。我知道我可以使用trace来查看表达式中第一级之外会发生什么,但是使用较长的表达式会有所不同,有时很复杂。

似乎唯一的方法是使用Extract,Part或Level提取并完全拆除表达式到列表中;评估我想要的部分表达;然后为每个阶段重新构建并重新映射表达式。是否有其他方法或功能可以实现这一点我可以考虑?

编辑:这可能是一个更好的例子来看待释放第一次保持的方法。用表达式:

Hold[Plus[Plus[2, Plus[2,2]], 2]]]

如果您释放第一个保留并在第三个Plus的表达式中保持较高级别,则如下所示:

in = Plus[Plus[2, Hold[Plus[2,2]]], 2]]]
out = Hold[2+2]+4

您发现Mathematica会在您真正希望等待时在背景中评估较低级别。

4 个答案:

答案 0 :(得分:3)

我无法给出Evaluate“仅在第一级上工作的确切原因,直接在保持函数内”但我怀疑它部分效率,因为如果评估者必须扫描它会很慢保存参数的完整表达式树,传递给嵌套Hold*表达式具有Evaluate属性的任何函数并对其进行评估,然后递归并查找刚刚评估的Evaluate子表达式,所有同时保持表达的其余部分不被评估,特别是当这可能并不总是你想要发生的事情时。

使用ExtractReplacePart的组合可以很轻松地执行您想要的操作:

In[51]:= expr = Hold[Plus[Plus[2, 2], 2]];

In[52]:= ReleaseHoldAt[expr_, partspec_] :=
  ReplacePart[expr, partspec -> Extract[expr, partspec]]

In[53]:= ReleaseHoldAt[expr, {1, 1}]

Out[53]= Hold[4 + 2]

这让我们说明了为什么Evaluate在作为具有Hold*属性的函数的参数传递的表达式中的任何级别工作可能没有意义的另一个原因,考虑到以下表达式涉及i

In[82]:= i = 1;

In[83]:= ReleaseHoldAt[Hold[i = 2; j = Plus[i, i]], {1, 2}]

Out[83]= Hold[i = 2; 2]

请注意,如果我们在j之前评估了该表达式的第一部分,4的值将为Plus,但结果不同,因为我们只做了部分评估时,我们评估子表达式设置i=2时未评估j。有时,这可能是你想要发生的事情,但通常很可能不会发生。

请注意,第一级中的Evaluate可以被具有属性HoldAllComplete或使用HoldComplete的函数击败:

In[62]:= Hold[Evaluate[Plus[2,2]]]
Out[62]= Hold[4]

...对:

In[63]:= HoldComplete[Evaluate[Plus[2,2]]]
Out[63]= HoldComplete[Evaluate[2+2]]

最后,Trace的输出可能有点密集,但您可以通过在第二个参数中使用感兴趣的模式或符号来过滤掉您想要的内容:

In[88]:= Trace[Plus[Plus[Plus[1,2],3],4],Plus]
Out[88]= {{{1+2,3},3+3,6},6+4,10}

In[93]:= Trace[Plus[Subtract[Plus[1,2],4],8],_Plus]
Out[93]= {{{1+2}},-1+8}

HTH!

答案 1 :(得分:3)

当您想在Mathematica中做一些棘手的事情时,模式匹配和规则替换的情况经常发生。但是,在这种情况下,您必须执行something weird,并且必须使用Replace而不是ReplaceAll/.运算符),以便您可以利用其可选项给它一个级别规范的第三个参数。使用您提供的示例:

In[1]:= Replace[
         Hold[Plus[Plus[2, 2], 2]],
         expr_Plus :> With[{eval = expr}, eval /; True],
         {2}]
Out[1]= Hold[4 + 2]

无用的

expr_Plus :> With[{eval = expr}, eval /; True]

规则实际上是a documented way,用于在测试匹配和With结构体之间共享局部变量;在这里你不会对局部变量做任何事情,而是以迂回的方式强制进行评估 - 因为较少的迂回方式不起作用!

编辑添加:我认为您错误地解释了Level的结果;此表达式{2}级别的两个表达式为2Plus[2, 2];你可以通过使用level的可选第三个参数来看到这一点,它类似于Extract的可选第三个参数:

In[2]:= Level[Hold[Plus[Plus[2, 2], 2]], {2}, Hold]
Out[2]= Hold[2 + 2, 2]

使用{2}级别规范,Replace将尝试根据这两个表达式匹配并替换规则,并且它将在第二个表达式上运行。

答案 2 :(得分:2)

不涉及Extract的技术是将Hold内的部分包裹在内部Hold中,然后释放外部Hold

expr=Hold[(1+2)+3];
ReleaseHold@Map[Hold,expr,{2}]

Out[2]= Hold[3]+Hold[1+2]

你可以在这个方向上玩各种游戏,但由于我不知道你想做什么,所以有点难以具体。可能有用的东西是定义你想要的下降的Hold

SetAttributes[DescentHold,{HoldAll}]
DescentHold[a_Plus]:=ReleaseHold@Map[DescentHold,Hold[a],{2}]
DescentHold[a_]:=Hold[a]

请注意,一旦内部被包裹,这个接近外部Hold,以便例如Plus的平坦度开始:

DescentHold[2*3+(4+5)]
Out[4]= Hold[4]+Hold[5]+Hold[2*3]

答案 3 :(得分:2)

使用来自answer Michael PilatReplacePartExtract函数的想法,可以编写HoldAndEvaluate函数,这可以让他评估所需的表达式的一部分,无需计算其位置(可以用“MyEvaluate”标记)。

In[1]:= expr = Hold[MyEvaluate[2 + 2] + 2];

In[2]:= HoldAndEvaluate[expr_] :=
  ReplacePart[expr,
    # -> Evaluate @@ Extract[expr, #] & /@ 
    Position[expr, MyEvaluate[_]] ];

In[3]:= HoldAndEvaluate[expr]

Out[3]= Hold[4 + 2]