为什么RuleDelayed没有持有Unevaluated?

Ale*_*kov 7 wolfram-mathematica

数学的评估器通常保持(或还原?)Head小号Unevaluated作为参数中提供的表达式的SymbolS:

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]]}
Run Code Online (Sandbox Code Playgroud)

但事实并非如此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
Run Code Online (Sandbox Code Playgroud)

为什么评估者会在任何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}
Run Code Online (Sandbox Code Playgroud)

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

Sas*_*sha 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]]}
Run Code Online (Sandbox Code Playgroud)

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

In[11]:= DownValues[f]

Out[11]= {HoldPattern[f[s_]] :> 1 + 1}
Run Code Online (Sandbox Code Playgroud)

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


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

Unevaluated是一种惰性符号,一种由评估者识别并在规则内作用的代币.由于这个原因Unevaluated[1+1]没有改变,以及f[1,Unevaluated[1+1]].在RuleDelayed[s,Unevaluated[1+1]]评估时,将Unevaluated被剥离,然后RuleDelayed根据评估原则重新评估整个表达式.


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

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

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

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.
Run Code Online (Sandbox Code Playgroud)

}


Leo*_*rin 7

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

为什么这个问题不重要

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

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

Out[186]= h[1, Unevaluated[Unevaluated[Unevaluated[1 + 1]]]]
Run Code Online (Sandbox Code Playgroud)

In[209]:= 1:>Unevaluated@Unevaluated@Unevaluated[1+1]

Out[209]= 1:>1+1
Run Code Online (Sandbox Code Playgroud)

关于剥离Unevaluated包装纸的一些细节

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

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

In[188]:= ClearAll[f];
f[Unevaluated[1+1]]

Out[189]= f[Unevaluated[1+1]]
Run Code Online (Sandbox Code Playgroud)

一个可以确认Unevaluated的包装被剥离,然后还原,通过使用TraceTraceOriginal选项设置为True:

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

Out[190]= {f[Unevaluated[1+1]],{f},f[1+1],f[Unevaluated[1+1]]} 
Run Code Online (Sandbox Code Playgroud)

当为某些规则定义时会发生什么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]]}
Run Code Online (Sandbox Code Playgroud)

如果一个人确切地知道表达式的特定部分将进行多少次评估,那么原则上可以将该部分包含在那么多层中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];
Run Code Online (Sandbox Code Playgroud)

(对于其他人来说,它可能没有任何评估泄漏,但除此之外.我也无法做到HoldRest,就像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]
Run Code Online (Sandbox Code Playgroud)

这间接支持了我的论点,即可能是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}    
Run Code Online (Sandbox Code Playgroud)

跟踪结果非常相似.我曾经DeleteCases过滤掉中间评估rd.差异是由于HoldAllComplete属性rdHoldRestRuleDelayed.