据我所知(其他回答者已经提到过这一点),Condition
不应将其视为独立函数,而应将其视为用于形成涉及模式的较大表达式的包装器。但我想强调的是,这里的部分微妙之处来自于这样一个事实:Rule
and RuleDelayed
是范围构造。一般来说,作用域构造必须有一个变量绑定阶段,在该阶段它们解决变量名称中可能存在的冲突,并实际上将变量绑定到它们在作用域构造体中的出现(或者,在规则的右侧)Rule
and RuleDelayed
)。这可能被认为是作用域构造内部工作的一部分,但是,因为 Mathematica 允许通过属性和类似的东西进行顶级操作Evaluate
,作用域构造并不像它们看起来那样黑盒 - 我们可以通过强制变量声明或主体或两者在绑定发生之前进行评估来更改绑定 - 例如,通过删除一些Hold*
- 属性。我讨论了这些事情here http://forums.wolfram.com/mathgroup/archive/2010/May/msg00206.html更详细地说,虽然我不知道范围构造的确切实现细节,但我不得不大部分猜测。
回到案例Rule
, RuleDelayed
and 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
and $ConditionHold
,出现时Condition
与使用Rule
or RuleDelayed
。我的猜测是,它们实现了将条件合并到模式变量上的机制,包括变量绑定。当你使用Condition
作为一个独立的功能,这些不会出现。这些头对于条件机制真正发挥作用至关重要。
你可以看看他们是如何工作的Rule
and 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
作为一个独立的功能是不同的 - 因此结果也不同。
我知道的一个很好的例子,很好地说明了上述几点,是这个线程 http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/3a5ae92bda1c7511,在可能的情况下实现一个版本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]]
(这是巴斯蒂安·埃德努斯的功劳)。这里发生的事情是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
如果它工作正常,我们最终应该得到两个不同的定义。到这里,我们就来到了问题的关键:既然如此Let
在运行时起作用,SetDelayed
没有察觉到Condition
作为模式的一部分 - 它会这样做With
, Block
, 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
在定义时,而不是运行时,这种情况就会发生before绑定阶段。现在,让我们看看:
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
and $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
(它吃掉任意数量的Unevaluated
r.h.s. 周围的包装器),在评论中指出这个问题 https://stackoverflow.com/questions/5036907/how-to-make-an-analog-of-instring.
总结一下:最常见的预期用途之一Condition
与封闭的范围结构密切相关(Rule
and RuleDelayed
),在分析其行为时,应考虑作用域构造中的变量绑定阶段。