条件问题(/;)

时间:2011-05-03 06:44:29

标签: wolfram-mathematica

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

这是我对所有案例的预期。

2 个答案:

答案 0 :(得分:6)

据我所知(其他答案者已经提到过),Condition不应该被认为是一个独立的函数,而应该被用作形成涉及模式的更大表达式的包装器。但我想强调,这里的一部分细微之处来自RuleRuleDelayed是范围构造的事实。通常,作用域构造必须具有变量绑定阶段,它们可以解决变量名中可能存在的冲突,并实际将变量绑定到作用域构造体中的出现位置(或者在Rule规则的rhs中和RuleDelayed)。这可能被视为范围构造的内部工作的一部分,但是,因为Mathematica允许通过属性和诸如Evaluate之类的东西进行顶级操作,所以范围构造不像它们看起来那样黑盒子 - 我们可能通过强制变量声明或正文或两者来更改绑定,以便在绑定发生之前进行评估 - 例如,通过删除一些Hold* - 属性。我更详细地讨论了这些事情here,虽然不知道范围构造的确切实现细节,但我不得不猜测。

回到RuleRuleDelayedCondition的情况,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,当ConditionRuleRuleDelayed一起使用时会显示。我的猜测是,这些实现了将模式变量包含条件的机制,包括变量绑定。当您使用Condition作为独立功能时,这些功能不会出现。这些头对于真正起作用的条件机制至关重要。 您可以在RuleRuleDelayed中查看它们的工作方式:

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视为模式的一部分 - 它会这样做WithBlockModule,但不是某些未知的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最常见的用途之一与封闭的范围构造(RuleRuleDelayed)密切相关,并且应考虑变量绑定在分析他们的行为时,确定范围构造的阶段。

答案 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用法相关。