Ale*_*kov 10 wolfram-mathematica
Condition具有HoldAll在应用之前阻止评估其第一个参数的属性Condition.但由于某种原因,Condition即使测试给出了评估它的第一个参数False:
In[1]:= Condition[Print[x],False]
During evaluation of In[1]:= x
Out[1]= Null/;False
Run Code Online (Sandbox Code Playgroud)
为什么是这样?Condition如果测试给出的话,为了什么目的评估它的第一个参数False?在哪些情况下这种行为可能有用?
PS当它Condition被用作第二个参数时,它的行为会有所不同SetDelayed:
In[5]:= f:=Condition[Print[x],False]; f
Out[6]= f
Run Code Online (Sandbox Code Playgroud)
这是我对所有案例的预期.
据我所知(已经有其他回答者已经提到过),Condition不应该将其视为独立函数,而应该将其视为用于形成涉及模式的更大表达式的包装器.但是我想要强调的是,这里的一部分细微之处来自于这一事实,Rule并且RuleDelayed 是范围构造.通常,作用域构造必须具有变量绑定阶段,在这里它们可以解决变量名中的可能冲突,并且实际上将变量绑定到它们在作用域构造体中的出现(或者在规则的rhs中为Rule和RuleDelayed).这可能被视为范围构造的内部工作的一部分,但是,因为Mathematica允许通过属性和事物进行顶级操作Evaluate,因此范围构造不像它们看起来那样是黑盒子 - 我们可以通过强制更改绑定在绑定发生之前评估的变量声明或正文或两者 - 例如,通过删除一些Hold*- 属性.我在这里更详细地讨论了这些内容,但是,不知道范围构造的确切实现细节,我不得不大多猜测.
回到案例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}}
Run Code Online (Sandbox Code Playgroud)
你看到的是,有特殊的内部头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
Run Code Online (Sandbox Code Playgroud)
例如,您可以看到Cases只选取表单中的元素$ConditionHold[$ConditionHold[something]],并忽略那些RuleCondition结果中的元素Fail.现在,当您Condition作为独立函数使用时会发生什么不同 - 因此结果的差异.
我所知道的一个很好的例子就是在这个线程中,这个例子很好地说明了上述几点,其中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]
Run Code Online (Sandbox Code Playgroud)
我们应该得到
a^2+(1+a+b)^2
Run Code Online (Sandbox Code Playgroud)
建议的一个实现,并给出了这个结果,是:
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]]
Run Code Online (Sandbox Code Playgroud)
(这是由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
Run Code Online (Sandbox Code Playgroud)
如果它正常工作,我们应该最终得到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
Run Code Online (Sandbox Code Playgroud)
如果你跟踪最后一次执行,那么很不清楚为什么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]]]];
Run Code Online (Sandbox Code Playgroud)
它的作用是扩展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}
Run Code Online (Sandbox Code Playgroud)
这很好用,你可以看到头部RuleCondition并$ConditionHold显示正确.查看结果定义是有益的ff:
?ff
Global`ff
ff[x_,y_]:=With[{xl=x},With[{yl=y+xl+1},xl^2+yl^2/;xl+yl<15]]
Run Code Online (Sandbox Code Playgroud)
你可以看到它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
Run Code Online (Sandbox Code Playgroud)
我们看到Mathematica现在认为这些模式是不同的.
最后一个问题是为什么Unevaluated不RuleDelayed通过删除其HoldRest属性来恢复被破坏的行为.我只能猜测这与这个异常的行为有关RuleDelayed(它会吞噬Unevaluatedrhs周围的任何数量的包装),在这个问题的评论中指出 .
总结:最常见的预期用途之一Condition与封闭的范围构造(Rule和RuleDelayed)紧密相关,并且在分析其行为时应考虑范围构造中的变量绑定阶段.
Condition使用通常取决于左侧的内容,因此它必须至少在某种程度上评估 LHS。考虑:
MatchQ[3, a_ /; IntegerQ[a]]
Run Code Online (Sandbox Code Playgroud)
真的
p = {a_, b_};
MatchQ[{3, 0.2}, p /; IntegerQ[a] && b < 1]
Run Code Online (Sandbox Code Playgroud)
真的
无论是对于这个还是从这个,我都会猜测它Condition具有属性HoldRest而不是HoldAll. 它可能需要HoldAll一些内部使用,可能与使用有关SetDelayed。