评估订单
这应该尽可能明确地解释评估顺序。它可能不太适合作为 Mathematica 执行的介绍。它建立在 Mathematica 的 tutorial/Evaluation
页面上,解释了应用不同规则的顺序,并解释了评估引擎特别处理的功能。
评估上下文
在任何时候,代码都在上下文中执行。上下文由一组替换规则(其字典)和应评估它们的优先级组成。我们将五种具有不同行为和限制的上下文分组:*设置上下文*块上下文*匹配上下文* ReplaceAll Context * ReplaceRepeated Context
设置上下文
Set Context 是由一组 Set []操作定义的(该函数更常用于写入 =
)。主要的例子是主要执行环境。只要在不是作用域的变量上运行 Set [],就会修改其字典。Set Contexts 的其他实例来自包。包上下文与其父上下文相关,因为包中的任何模式也成为父上下文中的模式,具有适当的前缀(定义 foo[x_]=3*x
变为 InnerPackageName\
foo [x _] = 3 * x`)。
设置上下文只能包含具有关联标记的规则,这是与头部关联的字符串,可以更快地识别规则的适用性。Set[yyy_,zzz_]
的应用将通过检查 yyy
是否为符号来确定标签。如果是,那就是标签。否则,它会检查 yyy[[0]]
,然后检查 yyy[[0,0]]
,依此类推。如果它在某个时刻被确定为符号,则将其作为标记。如果它是一个非符号原子(如 String,Integer,Real …),那么它将抛出一个错误,并且不会创建任何规则。
函数 UpSet(写入^=
)和 TagSet(写入/:
/ =
)(以及它们的兄弟 UpSetDelayed 和 TagSetDelayed)允许将规则与不同的 Tag 相关联。仍然存在标签必须是符号的限制。UpSet 会将它与表达式中的每个参数相关联,或者如果它们是具有头部符号的函数,则将它们与头部相关联。例如,在 f[a,b,c+d,e[f,g],5,h[i][j][k],p_]
上调用 UpSet 会将创建的规则与 a
,b
,e
和 h
相关联。c+d
,5
和 p_
参数与它们没有任何关联,并将导致显示错误消息。对于每个参数,赋值仍然会成功,并且(稍后将在评估顺序中明确说明)它几乎可以用于所有目的。TagSet 就像 UpSet,但你可以为 Tag 指定一个符号。符号必须仍然可以由 Set 或 UpSet(头部或参数中的顶级符号)设置。例如,TagSet[f, f[a,b[c]], 2]
是可以接受的,并将定义与 f
相关联; TagSet[a, f[a,b[c]], 2]
和 TagSet[b, f[a,b[c]], 2]
也可以接受,但是 TagSet[c, f[a,b[c]], 2]
不是。
Context 中的规则需要应用程序优先级,因为可以有许多规则适用于给定的表达式。 (在 ReplaceAll 和 ReplaceRepeated Contexts 中也是如此,但它们的处理方式却截然不同)。优先级通常旨在对应于特异性的模式。给出一个表达 a[q][b[c,d],e[f,g]]
进行评估,将头部和参数全部评估为完全(参见下面的 TODO),首先查找标记为 b
的规则; 然后用 e
标记规则; 然后用 a
标记规则。在每组规则中,维持与给定符号相关联的订单。没有空格的规则(例如 f[a,b]=3
)会自动放在顶部,并按规范顺序排序(排序顺序)。每次添加新规则时,内核都会通过列表; 如果某些规则具有完全相同的 LHS,则它将被就地替换。否则,它进行特异性比较。如果列表中已经存在的规则 X 被确定为比新规则 Y更不具体,则 Y 紧接在 X 之前。否则,它继续通过列表。如果没有特定规则,则规则将放在列表的末尾。特异性检查更复杂,并在下面的部分中更详细地给出。
规则特异性
*如果两个表达式没有 BlankSequence(__
),BlankNullSequence(___
),Optional(:
),Alternatives(|
),Repeated(..
),RepeatedNull(...
)或可选参数(_.
)的实例,那么它们可以进行比较结构。给定两个等价的表达式树 X 和 Y,其中 Y 中的所有空白也是 X 中的空白,但是 X 具有空白而 Y 不具有空白,则 Y 更具体。 *如果两个表达式是等价的,除了 _
的某些实例已经被另一个表达式中的 __
替换,或者 __
已被替换为 ___
,那么前者更具体。 *如果剥离一个可选(:
)或可选(_.
)术语给出另一个表达式,那么后者更具体。 *如果来自 Alternatives 的某些选择给出了另一个表达式,那么后者更具体。 *如果用 Repeated[foo]
替换 RepeatedNull[foo]
的所有实例,或用 foo
替换 Repeated[foo]
,则给出另一个表达式,然后后者更具体*这些规则的某些组合可以立即应用,但目前还不知道这是什么情况。 *理论上,如 _List
和 {___}
这样的表达组合应该对它们进行相同的处理,但这种比较似乎是奇怪的依赖于上下文,偶尔会以某种方式对它们进行排序。
阻止上下文
块上下文更具限制性,因为块中规则的 LHS 只能是符号。也就是说,只有形式 f=2+x
的定义,而不是 f[x_]=2+x
。 (注意,从实际的角度来看,函数仍然可以使用诸如`Set [Block 与其父上下文相关的定义]来构造,因为在评估 Block 期间的任何新定义都会正常转发到包围的上下文,但是将遮蔽一些变量集,提供可以隐藏周围上下文的定义。在评估内部表达式时,仍然可以访问周围上下文中的定义。因为只能定义与符号相关的定义,所以没有概念优先权如上。
匹配上下文
匹配规则后,存在变量的本地绑定定义。这在词汇上发生。也就是说,它在表达式中的变量的绑定定义中排除,而不评估任何其他内容。只有在所有的替换都发生之后,它才会从顶部再次开始评估整个表达式。创建匹配上下文的主要方式是来自设置上下文或规则的规则。例如,在
g[a_]:=a+x;
f[x_]:=x+g[1];
f[x^2]
(*Yields 1+x+x^2 *)
将 f[x_]
规则与 f[y]
匹配后,符号 x
将绑定到值 x^2
。它执行一次替换,但由于它不评估 g
,它返回 x^2+g[1]
。然后再次在周围的 Set Context 中对其进行评估,并成为 1+x+x^2
。匹配上下文中的评估的显着差异在于替换不是递归的。当它替代 x->x^2
时,它甚至不会重复它自己的结果。
匹配上下文也由 With
,Module
,Function
和 Replace
创建。许多其他函数在内部创建它们,例如 Plot
在评估要绘制的表达式时使用此类上下文。
ReplaceRepeated Context
当 ReplaceRepeated
的任何应用程序发生时,将创建 ReplaceRepeated Context。这是不同的,因为它可以具有任何表达式作为规则 LHS,包括那些没有标签的表达式,例如 _[_]
。从这个意义上说,它是最灵活的背景。它还可以包含几个可能冲突的规则,因此必须保持优先级。如果适用,ReplaceRepeated Context 将首先应用列表中的第一个规则。如果它不匹配,则进入第二个,依此类推。如果规则在任何时候匹配,则返回第一个规则并再次开始。如果在任何时候发生规则应用程序并且没有发生任何更改,它将退出 - 即使列表中稍后的其他规则将进行更改。这意味着列表中较早的任何不太具体的规则将阻止以后的规则被使用。另外,
替换所有上下文
当 ReplaceAll
的任何应用程序发生时,将创建 ReplaceAll Context。它的功能类似于 ReplaceRepeated 的功能,因为当两个都可以在表达式的同一级别应用时,它的规则应用程序优先级按顺序排列。然而,它就像匹配上下文一样,被替换的内容不会被进一步评估,甚至不被后来的规则评估 ** 。例如,x/.{x->y,y->z}
产生 y
。因此,查看 ReplaceAll 的应用程序依次应用每个规则是不正确的。相反,它遍历树,寻找适用的规则。当它找到匹配的东西时,它会执行替换,然后返回树,而不遍历新树。值得注意的是,它试图从上到下应用规则,结果可能不按列表的顺序排列。例如,
Cos[1 + 2 Sqrt[Sin[x]]] /. {Cos[_] -> 5, Sin[_] :> (Print[1]; 10)}
Cos[1 + 2 Sqrt[Sin[x]]] /. {Sin[_] :> (Print[1]; 10), Cos[_] -> 5}
两者都没有打印任何东西而产生 5
。因为 Cos[_]
匹配树的更高级别,所以它首先应用该树。
Hold
和 Evaluate
以及执行顺序
给定表达式的评估顺序如下:*尽可能彻底地评估头部*如果头部具有 Hold
属性(HoldFirst
,HoldRest
,HoldAll
,HoldAllComplete
),则*检查相关参数。除非它是 HoldAllComplete
,检查头是否是 Evaluate
。如果是,则剥去 Evaluate
并将其标记为无论如何要进行评估。 *检查 Unevaluated
实例的参数并根据需要剥离它们,除非属性 HoldAllComplete
存在。 *除非使用 SequenceHold
,否则从 Sequence
s 中展平参数。 *根据情况应用 Flat
,Listable
,Orderless
属性。 *应用与参数的 upvalues 相关的评估(他们的标签)*应用与头部相关的评估。
当表达式的评估完成时,它被标记为完全评估。达到该表达式的后续评估不会尝试评估它。如果在内部发生的符号上定义了新规则,则会删除该标志,并且可以再次对其进行评估。这个’失败’的显着位置是 Condition
(\;
):条件规则可能不适用于初始评估。如果不相关的符号更改值并且现在条件适用,则表达式仍标记为完全计算,并且不会因此而更改。函数 Update
的独特之处在于它将评估其参数,无论其已经评估过的状态如何,都会强制进行缓存刷新。
还有许多其他功能通常被视为例外,例如 Hold
,Defer
,ReplacePart
,Extract
或 ReleaseHold
。这些效果都可以通过属性(例如 HoldAll
)和普通函数定义来实现,并且不需要由评估者唯一地处理。