据我所知(其他回答者已经提到了这一点),Condition 不应被视为独立函数,而应被视为用于形成涉及模式的更大表达式的包装器。但我想强调,这里的部分微妙之处在于Rule 和RuleDelayed 是范围构造。一般来说,作用域构造必须有一个变量绑定阶段,在此阶段它们解决变量名称中可能存在的冲突,并将变量实际绑定到它们在作用域构造体中的出现处(或者,在Rule 和@ 规则的右上方) 987654328@)。这可能被认为是作用域构造内部工作的一部分,但是,因为 Mathematica 允许通过属性和诸如Evaluate 之类的东西进行顶级操作,作用域构造并不像它们看起来的那样是黑盒子——我们可以改变通过强制变量声明或主体或两者在绑定发生之前评估绑定 - 例如,通过删除一些Hold* - 属性。我更详细地讨论了here 这些事情,虽然我不知道范围构造的确切实现细节,但我主要是猜测。
回到Rule、RuleDelayed 和Condition 的情况,对Trace 讨论的示例之一具有指导意义:
In[28]:= Trace[Cases[{3,3.},a_:>Print[a]/;(Print["!"];IntegerQ[a])],RuleCondition,TraceAbove->All]
During evaluation of In[28]:= !
During evaluation of In[28]:= !
During evaluation of In[28]:= 3
Out[28]= {Cases[{3,3.},a_:>Print[a]/;(Print[!];IntegerQ[a])],
{RuleCondition[$ConditionHold[$ConditionHold[Print[3]]],True],
$ConditionHold[$ConditionHold[Print[3]]]},
{RuleCondition[$ConditionHold[$ConditionHold[Print[3.]]],False],Fail},
{Print[3]},{Null}}
你看到的是有特殊的内部头RuleCondition和$ConditionHold,当Condition与Rule或RuleDelayed一起使用时会出现。我的猜测是这些实现了将条件合并到模式变量上的机制,包括变量绑定。当您将Condition 用作独立函数时,这些不会出现。这些磁头对于条件机制真正起作用至关重要。
您可以在Rule 和RuleDelayed 中查看它们的工作原理:
In[31]:= RuleCondition[$ConditionHold[$ConditionHold[Print[3.`]]],True]
Out[31]= $ConditionHold[$ConditionHold[Print[3.]]]
In[32]:= RuleCondition[$ConditionHold[$ConditionHold[Print[3.`]]],False]
Out[32]= Fail
你可以看到,比如说,Cases 只选择$ConditionHold[$ConditionHold[something]] 形式的元素,而忽略那些RuleCondition 导致Fail 的元素。现在,当您将 Condition 用作独立函数时会发生什么不同 - 因此结果会有所不同。
我知道的一个很好的例子,它很好地说明了上述几点,在this thread 中,讨论了按顺序绑定的With 版本的可能实现。我将在这里重复部分讨论,因为它具有指导意义。这个想法是制作一个版本的 With,以前的声明可以用于声明列表中更靠后的声明。如果我们称它为Let,那么,例如,对于类似的代码
Clear[h, xl, yl];
xl = 1;
yl = 2;
h[x_, y_] := Let[{xl = x, yl = y + xl + 1}, xl^2 + yl^2];
h[a, b]
我们应该得到
a^2+(1+a+b)^2
建议并给出此结果的实现之一是:
ClearAll[Let];
SetAttributes[Let, HoldAll];
Let /: (lhs_ := Let[vars_, expr_ /; cond_]) :=
Let[vars, lhs := expr /; cond]
Let[{}, expr_] := expr;
Let[{head_}, expr_] := With[{head}, expr]
Let[{head_, tail__}, expr_] := With[{head}, Let[{tail}, expr]]
(这是由于 Bastian Erdnuess)。这里发生的是 Let 在运行时执行绑定,而不是在定义函数时。一旦我们想使用共享局部变量,它就会失败:
Clear[f];
f[x_,y_]:=Let[{xl=x,yl=y+xl+1},xl^2+yl^2/;(xl+yl<15)];
f[x_,y_]:=x+y;
?f
Global`f
f[x_,y_]:=x+y
如果它工作正常,我们应该得到 2 个不同的定义。这里我们来到了问题的症结所在:由于这个Let 在运行时起作用,SetDelayed 不会将Condition 视为模式的一部分——它会为With、@987654361 做到这一点@,Module,但不是一些未知的Let。因此,两个定义对 Mathematica 的查找都是相同的(就模式而言),因此,第二个定义取代了第一个定义。但这并不是全部。现在我们只创建第一个定义,并尝试执行:
Clear[f];
f[x_, y_] := Let[{xl = x, yl = y + xl + 1}, xl^2 + yl^2 /; (xl + yl < 15)];
In[121]:= f[3, 4]
Out[121]= 73 /; 3 + 8 < 15
如果您跟踪最后一次执行,将非常不清楚为什么 Condition 没有在这里触发。原因是我们搞砸了绑定阶段。这是我的改进版本,没有这些缺陷:
ClearAll[LetL];
SetAttributes[LetL, HoldAll];
LetL /: Verbatim[SetDelayed][lhs_, rhs : HoldPattern[LetL[{__}, _]]] :=
Block[{With}, Attributes[With] = {HoldAll};
lhs := Evaluate[rhs]];
LetL[{}, expr_] := expr;
LetL[{head_}, expr_] := With[{head}, expr];
LetL[{head_, tail__}, expr_] :=
Block[{With}, Attributes[With] = {HoldAll};
With[{head}, Evaluate[LetL[{tail}, expr]]]];
它的作用是在定义时而不是运行时将LetL 扩展为嵌套的With,这发生在在绑定阶段之前。现在,让我们看看:
In[122]:=
Clear[ff];
ff[x_,y_]:=LetL[{xl=x,yl=y+xl+1},xl^2+yl^2/;(xl+yl<15)];
Trace[ff[3,4]]
Out[124]= {ff[3,4],
{With[{xl$=3},With[{yl$=4+xl$+1},RuleCondition[$ConditionHold[$ConditionHold[xl$^2+yl$^2]],
xl$+yl$<15]]],With[{yl$=4+3+1},RuleCondition[$ConditionHold[$ConditionHold[3^2+yl$^2]],3+yl$<15]],
{4+3+1,8},RuleCondition[$ConditionHold[$ConditionHold[3^2+8^2]],3+8<15],
{{3+8,11},11<15,True},RuleCondition[$ConditionHold[$ConditionHold[3^2+8^2]],True],
$ConditionHold[$ConditionHold[3^2+8^2]]},3^2+8^2,{3^2,9},{8^2,64},9+64,73}
这很好用,您可以看到头像RuleCondition 和$ConditionHold 正常显示。查看ff 的结果定义很有启发性:
?ff
Global`ff
ff[x_,y_]:=With[{xl=x},With[{yl=y+xl+1},xl^2+yl^2/;xl+yl<15]]
您可以看到 LetL 在定义时已扩展,正如所宣传的那样。并且由于在那之后发生了模式变量绑定,所以一切正常。另外,如果我们添加另一个定义:
ff[x_,y_]:=x+y;
?ff
Global`ff
ff[x_,y_]:=With[{xl=x},With[{yl=y+xl+1},xl^2+yl^2/;xl+yl<15]]
ff[x_,y_]:=x+y
我们看到,这些模式现在被 Mathematica 认为是不同的。
最后一个问题是为什么 Unevaluated 不会恢复 RuleDelayed 的行为,因为删除了它的 HoldRest 属性。我只能猜测这与 RuleDelayed 的不寻常行为有关(它会吞噬 r.h.s. 周围的任意数量的 Unevaluated 包装器),在 this question 的 cmets 中注明。
总结一下:Condition 最常见的预期用途之一与封闭的范围构造(Rule 和 RuleDelayed)密切相关,并且应该考虑范围构造中的变量绑定阶段,当分析他们的行为。