Condition
具有属性HoldAll
,可防止在应用Condition
之前评估其第一个参数。但由于某种原因Condition
评估其第一个参数,即使测试给出False
:
In[1]:= Condition[Print[x],False]
During evaluation of In[1]:= x
Out[1]= Null/;False
这是为什么?出于什么目的Condition
如果测试给出False
,则评估其第一个参数?在哪些情况下这种行为可能有用?
P.S。当Condition
用作SetDelayed
的第二个参数时,其行为会有所不同:
In[5]:= f:=Condition[Print[x],False]; f
Out[6]= f
这是我对所有案例的预期。
答案 0 :(得分:6)
据我所知(其他答案者已经提到过),Condition
不应该被认为是一个独立的函数,而应该被用作形成涉及模式的更大表达式的包装器。但我想强调,这里的一部分细微之处来自Rule
和RuleDelayed
是范围构造的事实。通常,作用域构造必须具有变量绑定阶段,它们可以解决变量名中可能存在的冲突,并实际将变量绑定到作用域构造体中的出现位置(或者在Rule
规则的rhs中和RuleDelayed
)。这可能被视为范围构造的内部工作的一部分,但是,因为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
,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
,而不是在运行时,并且在绑定阶段之前发生。现在,让我们看看:
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
的异常行为有关(它会占用rhs周围任意数量的Unevaluated
包装器),在this question的评论中指出。
总结:Condition
最常见的用途之一与封闭的范围构造(Rule
和RuleDelayed
)密切相关,并且应考虑变量绑定在分析他们的行为时,确定范围构造的阶段。
答案 1 :(得分:4)
Condition
使用通常取决于左侧的内容,因此必须至少在某种程度上评估LHS。考虑:
MatchQ[3, a_ /; IntegerQ[a]]
True
p = {a_, b_};
MatchQ[{3, 0.2}, p /; IntegerQ[a] && b < 1]
True
对于此以及从此,我猜测Condition
具有属性HoldRest
而不是HoldAll
。它可能需要HoldAll
用于某些内部用途,可能与SetDelayed
用法相关。