为什么RuleDelayed没有持有Unevaluated?

时间:2011-06-07 14:53:30

标签: wolfram-mathematica

Mathematica 的评估者通常持有(或恢复?)作为Head s的参数提供的Unevaluated Symbol个表达式:

In[1]:= f[s, Unevaluated[1 + 1]]

Out[2]= f[s, Unevaluated[1 + 1]]

In[5]:= Trace[f[s,Unevaluated[1+1]],TraceOriginal->True]

Out[5]= {f[s,Unevaluated[1+1]],{f},{s},f[s,1+1],f[s,Unevaluated[1+1]]}

RuleDelayed并非如此。此外,在Unevaluated的情况下,任何RuleDelayed包装器都被剥离了:

In[1]:= Attributes@RuleDelayed
RuleDelayed[s, Unevaluated[1 + 1]]
RuleDelayed[s, Unevaluated@Unevaluated[1 + 1]]
RuleDelayed[s, Unevaluated@Unevaluated@Unevaluated[1 + 1]]
RuleDelayed[Unevaluated@Unevaluated@Unevaluated[1 + 1], 1 + 1]

Out[1]= {HoldRest, Protected, SequenceHold}

Out[2]= s :> 1 + 1

Out[3]= s :> 1 + 1

Out[4]= s :> 1 + 1

Out[5]= 2 :> 1 + 1

为什么评估者在Unevaluated的情况下剥离任何RuleDelayed包装器?它的用途是什么?是否可以为任意Symbol(例如f)模拟此类行为?


还不清楚为什么Trace显示的RuleDelayed图片比f更复杂:

In[2]:= Trace[RuleDelayed[s,1+1],TraceOriginal->True]
Out[2]= {s:>1+1,{RuleDelayed},{s},s:>1+1,s:>1+1,{RuleDelayed},{s},s:>1+1}

看起来RuleDelayed被评估两次......

3 个答案:

答案 0 :(得分:7)

Unevaluated在作为规则中最外层包装器出现时被剥离。这就是Unevaluated的工作原理,即Unevaluated不是评估任何内容的常规符号。这是一个象征。比较

In[8]:= f[s_] := g[Unevaluated[1 + 1]]

In[9]:= DownValues[f]

Out[9]= {HoldPattern[f[s_]] :> g[Unevaluated[1 + 1]]}

In[10]:= f[s_] := Unevaluated[1 + 1]

In[11]:= DownValues[f]

Out[11]= {HoldPattern[f[s_]] :> 1 + 1}

因为赋值器是递归的,所以最终摆脱Unevaluated就足够了。


编辑根据Alexey的评论扩大答案:

Unevaluated是一个惰性符号,一个由评估者识别并在规则内作用的标记。因此,Unevaluated[1+1]f[1,Unevaluated[1+1]]保持不变。评估RuleDelayed[s,Unevaluated[1+1]]后,Unevaluated将被删除,然后根据评估原则重新评估整个RuleDelayed表达式。


编辑2 这是关于重新评估原因的讨论的浓缩结果

`RuleDelayed`的实现细节导致重复评估,最终剥离Unevaluated。在我的回答下面的评论中,我提供了另一个命令的示例,该命令导致双重评估,原因完全相同。发生这种情况是因为表达式经过验证,一旦经过验证,就会标记一个有效的标志。设置有效标志会启动重新评估序列。这种情况一直发生,直到表达不再发生变化。

对于需要验证的其他表达式也会出现类似的效果,例如Root object:

In[41]:= Root[#1^6 + #1 - 1 & , 1]; Trace[Root[#1^6 + #1 - 1 & , 1], 
 TraceOriginal -> True]

Out[41]= {
 HoldForm[Root[#1^6 + #1 - 1 & , 1]], {HoldForm[Root]}, 
   {HoldForm[#1^6 + #1 - 1 & ], {HoldForm[Function]}, 
  HoldForm[#1^6 + #1 - 1 & ]}, 
   {HoldForm[1]}, 
  HoldForm[Root[#1^6 + #1 - 1 & , 1]],    <-- here the root had been 
                                              stamped valid, and reevaluated
   HoldForm[Root[-1 + #1 + #1^6 & , 1]]   <-- evaluation was trivial.

}

答案 1 :(得分:7)

这个答案应被视为@Sasha答案的补充。我认为这是一个微妙的话题,可以从几个观点的解释中受益。

为什么这个问题非常重要

我想强调的是,所讨论的行为并不典型,因为它不是大多数人在Mathematica中表现的方式,也不能仅基于评估的一般原则来解释(特别是{的力学) {1}}剥离),没有诉诸具有此类行为的特定头部的实现细节(Unevaluated此处)。考虑具有RuleDelayed属性的一般头部:

HoldRest

In[185]:= SetAttributes[h, HoldRest];
h[1, Unevaluated[Unevaluated[Unevaluated[1 + 1]]]]

Out[186]= h[1, Unevaluated[Unevaluated[Unevaluated[1 + 1]]]]

关于剥离In[209]:= 1:>Unevaluated@Unevaluated@Unevaluated[1+1] Out[209]= 1:>1+1 包装

的一些细节

这是基于David Wagner“Mathematica中的Power编程 - 内核”一书中的讨论,David Withoff的WRI技术报告“Mathematica internals”,以及我自己的经历。

这是一张非常简化的评估图片。 Mathematica递归地计算表达式,首先从“branches”(表达式)“向下”到“子分支”(子表达式)和离开(原子),然后“向上”。在“向下”的路上,评估(子)表达式的头部,然后评估部分。具有头Unevaluated的那些部分不会被进一步评估(在评估者没有递归地调用它们的意义上),而Unevaluated被剥离并且标记为已经完成。在“向上”的路上,认为已经评估了部件。有许多步骤,包括序列拼接,与UnevaluatedFlat等属性相关的评估。然后,应用,用户定义和内置的头部规则(用户定义和内置) OrderlessUpValuesDownValues)。最后,这对于本次讨论非常重要,SubValues包装器将针对那些没有找到适用规则的表达式部分进行恢复。这就是为什么,对于未定义的函数Unevaluated,我们有:

f

可以确认In[188]:= ClearAll[f]; f[Unevaluated[1+1]] Out[189]= f[Unevaluated[1+1]] 包装被剥离然后使用Unevaluated Trace选项设置为TraceOriginal进行恢复:

True

In[190]:= Trace[f[Unevaluated[1+1]],TraceOriginal->True] Out[190]= {f[Unevaluated[1+1]],{f},f[1+1],f[Unevaluated[1+1]]} 定义某些规则会发生什么?答案是每个规则应用程序剥离一层f。这是一个例子:

Unevaluated

如果一个人确切地知道表达式的特定部分将进行多少次评估,那么原则上可以将该部分包含在In[204]:= f[x_]:=Hold[x]; g[x_]:=f[x]; {f[Unevaluated[1+1]],g[Unevaluated[1+1]]} {f[Unevaluated@Unevaluated[1+1]],g[Unevaluated@Unevaluated[1+1]]} {f[Unevaluated@Unevaluated@Unevaluated[1+1]], g[Unevaluated@Unevaluated@Unevaluated[1+1]]} Out[206]= {Hold[1+1],Hold[2]} Out[207]= {Hold[Unevaluated[1+1]],Hold[1+1]} Out[208]= {Hold[Unevaluated[Unevaluated[1+1]]],Hold[Unevaluated[1+1]]} 的多个层中以防止其评估。但是,此信息通常不可能有,并且Unevaluated不应该用作持久保持包装 - 这是Unevaluated的用途。但是这种分析可以更清楚地表明,为了使任何数量的评估结束,执行该评估的负责人必须具有为其定义的非平凡规则。换句话说,通常情况下,评估过程中剥离一层Hold的部分不会(本身,“在表达式的下方”)诱导其重新评估 - 这种情况只会发生在由于为那个头定义了一些规则,“向上”的方式。结论是Unevaluated观察到的行为只能通过查看RuleDelayed的实现细节来解释,一般考虑因素是不够的。

插图:模拟RuleDelayed

的行为

我现在将说明这一点,并回答有关此行为模拟的原始问题的部分内容。据我所知,以下代码完全模拟了RuleDelayed关于剥离RuleDelayed包装器的行为:

Unevaluated

(对于其他人来说,它可能没有任何评估泄漏,但除了重点之外。另外,我无法像ClearAll[rd]; SetAttributes[rd, {HoldAllComplete, SequenceHold}]; rd[lhs_, Verbatim[Unevaluated][rhs_]] /; Head[Unevaluated[rhs]] =!= Unevaluated := Append[rd[lhs], Unevaluated[rhs]]; rd[lhs_, Verbatim[Unevaluated][rhs_]] := rd @@ {lhs, rhs}; rd[lhs_, rhs_] /; Hold[lhs] =!= Hold[Evaluate[lhs]] := Prepend[rd[rhs], lhs]; 那样HoldRest - 我不得不使用{ {1}}使这种结构起作用)。你可以查看:

RuleDelayed

这间接支持了我的论点,即可能是HoldAllComplete实现,而不是评估者,负责这种效果(虽然,我不能确定,我只能猜测。而且,In[173]:= a=1; rd[a,Unevaluated[1+1]] rd[a,Unevaluated@Unevaluated[1+1]] rd[a,Unevaluated@Unevaluated[1+1]] Out[174]= rd[1,1+1] Out[175]= rd[1,1+1] Out[176]= rd[1,1+1] 是足够基本的,这种特殊的行为可以连接到评估者身上)

修改

为了进一步强化类比,以下是跟踪结果:

RuleDelayed

跟踪结果非常相似。我使用RuleDeleayed来过滤In[183]:= DeleteCases[Trace[rd[s,Unevaluated[1+1]],TraceOriginal->True], x_/;!FreeQ[x,Head|Hold|Append|HoldPattern[rd[_]]]] Out[183]= {rd[s,Unevaluated[1+1]],{rd},rd[s,Unevaluated[1+1]], rd[s,1+1],{rd},rd[s,1+1],rd[s,1+1]} In[184]:= Trace[RuleDelayed[s,Unevaluated[1+1]],TraceOriginal->True] Out[184]= {s:>Unevaluated[1+1],{RuleDelayed},{s},s:>1+1,s:>1+1,{RuleDelayed},{s},s:>1+1} 的中间评估。差异是由DeleteCasesrd的{​​{1}} HoldAllComplete rd属性造成的。

答案 2 :(得分:4)

使用用户定义的函数“rd”

重现RuleDelayed的行为

根据Sasha的描述,这是重现RuleDelayed评估行为的简单方法:

  

&LT; ...&GT; [规则]表达经过验证,   经过验证,它上面印有   某个有效的旗帜。设置   有效标志启动重新评估   序列。这种情况一直持续到   表达不再变化。

ClearAll[rd];
SetAttributes[rd, {HoldRest, SequenceHold}];
Options[rd] = {"Validated" -> None};
expr : rd[args__] /; ("Validated" /. Options[Unevaluated[rd]]) =!= 
   Hold[expr] := (Options[
    Unevaluated[rd]] = {"Validated" -> Hold[expr]}; rd[args])

In[6]:= rd[Unevaluated@Unevaluated[1 + 1], 
 Unevaluated@Unevaluated[Unevaluated[1 + 1]]]

Out[6]= rd[2, 1 + 1]

我们可以比较rdRuleDelayed的第一个参数的评估次数:

dummyFunction /; (++numberOfEvaluations; False) := Null;

In[36]:= numberOfEvaluations=0;
rd[dummyFunction,Unevaluated@Unevaluated[Unevaluated[1+1]]];
numberOfEvaluations
numberOfEvaluations=0;
RuleDelayed[dummyFunction,Unevaluated@Unevaluated[Unevaluated[1+1]]];
numberOfEvaluations
Out[38]= 4
Out[41]= 4

跟踪rd和RuleDelayed

以下内容表明此版本几乎完全复制了RuleDelayed的行为。唯一的区别是对最终表达式rd[2,1+1]的最后一次额外评估,它涉及条件检查并且不给出匹配。使用rd作为Trace的第二个参数时,会自动排除此最后一次评估。在RuleDelayed的情况下,Trace无法捕获此最后一项检查,因为它不会通过评估程序。

代码:

ClearAll[rd];
SetAttributes[rd, {HoldRest, SequenceHold}];
SetAttributes[returnLast, {HoldRest}]
SetAttributes[{NotValidatedQ, setValidatedFlag}, HoldAllComplete];
Options[rd] = {"Validated" -> None};
NotValidatedQ[expr_] := ("Validated" /. Options[Unevaluated[rd]]) =!= 
   Hold[expr];
setValidatedFlag[expr_] := 
  Options[Unevaluated[rd]] = {"Validated" -> Hold[expr]};
returnLast[first_, last_] := last;
expr : rd[args__] /; NotValidatedQ[expr] := 
 returnLast[setValidatedFlag[expr], rd[args]]

比较:

rdList = DeleteCases[
   Trace[rd[Unevaluated@Unevaluated[1 + 1], 
     Unevaluated@Unevaluated[Unevaluated[1 + 1]]], 
    TraceOriginal -> 
     True], ({HoldForm[(validatedQ | setValidatedFlag)[_]], ___} | 
     HoldForm[_returnLast] | {HoldForm[returnLast]})] /. 
  rd -> RuleDelayed

RuleDelayedList = 
 Trace[RuleDelayed[Unevaluated@Unevaluated[1 + 1], 
   Unevaluated@Unevaluated[Unevaluated[1 + 1]]], 
  TraceOriginal -> True]

screenshot

使用Trace的第二个参数进行跟踪显示完全匹配:

In[52]:= rdList = 
  Trace[rd[Unevaluated@Unevaluated[1 + 1], 
     Unevaluated@Unevaluated[Unevaluated[1 + 1]]], rd, 
    TraceOriginal -> True] /. {HoldForm[_returnLast] -> Sequence[], 
    rd -> RuleDelayed};
RuleDelayedList = 
  Trace[RuleDelayed[Unevaluated@Unevaluated[1 + 1], 
    Unevaluated@Unevaluated[Unevaluated[1 + 1]]], RuleDelayed, 
   TraceOriginal -> True];

rdList === RuleDelayedList

Out[54]= True