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
被评估两次......
答案 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
就足够了。
Unevaluated
是一个惰性符号,一个由评估者识别并在规则内作用的标记。因此,Unevaluated[1+1]
和f[1,Unevaluated[1+1]]
保持不变。评估RuleDelayed[s,Unevaluated[1+1]]
后,Unevaluated
将被删除,然后根据评估原则重新评估整个RuleDelayed
表达式。
`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
被剥离并且标记为已经完成。在“向上”的路上,认为已经评估了部件。有许多步骤,包括序列拼接,与Unevaluated
,Flat
等属性相关的评估。然后,应用,用户定义和内置的头部规则(用户定义和内置) Orderless
,UpValues
,DownValues
)。最后,这对于本次讨论非常重要,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}
的中间评估。差异是由DeleteCases
与rd
的{{1}} HoldAllComplete
rd
属性造成的。
答案 2 :(得分:4)
根据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]
我们可以比较rd
和RuleDelayed
的第一个参数的评估次数:
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
以下内容表明此版本几乎完全复制了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]
使用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