【问题标题】:Question on Condition (/;)条件问题 (/;)
【发布时间】:2011-05-03 06:44:28
【问题描述】:

Condition 具有属性HoldAll,它可以防止在应用Condition 之前评估其第一个参数。但是由于某种原因,Condition 会评估它的第一个参数,即使测试给出了False

In[1]:= Condition[Print[x],False]
During evaluation of In[1]:= x
Out[1]= Null/;False

这是为什么?如果测试给出FalseCondition 会出于什么目的评估其第一个参数?这种行为在哪些情况下有用?

附:当Condition 用作SetDelayed 的第二个参数时,它的行为会有所不同:

In[5]:= f:=Condition[Print[x],False]; f
Out[6]= f

这是我对所有情况的预期。

【问题讨论】:

  • @Mr.wizard 早上好/下午/晚上/晚上! @Alexey我不了解HoldAll,但条件不应该只适用于模式,RuleDelayed 和SetDelayed,而不是其他任何东西?至少,这就是文档中的内容。您的使用没有记录。
  • @Sjoerd 我理解,但在 Mathematica 中,所有函数都应该以非常通用的方式工作。
  • 您可能知道,Ted Ersek 讨论了hereCondition 无关的微妙之处(以及Update 的用法)。 Carl Woll 的reply 对 David Park 的一个旧的数学组问题可能也很感兴趣。

标签: wolfram-mathematica


【解决方案1】:

据我所知(其他回答者已经提到了这一点),Condition 不应被视为独立函数,而应被视为用于形成涉及模式的更大表达式的包装器。但我想强调,这里的部分微妙之处在于RuleRuleDelayed 是范围构造。一般来说,作用域构造必须有一个变量绑定阶段,在此阶段它们解决变量名称中可能存在的冲突,并将变量实际绑定到它们在作用域构造体中的出现处(或者,在Rule 和@ 规则的右上方) 987654328@)。这可能被认为是作用域构造内部工作的一部分,但是,因为 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 视为模式的一部分——它会为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 最常见的预期用途之一与封闭的范围构造(RuleRuleDelayed)密切相关,并且应该考虑范围构造中的变量绑定阶段,当分析他们的行为。

【讨论】:

  • @Mr.Wizard 我不知道有什么好的简短的例子来说明我的观点,此外,这些都是我不完全理解的相当微妙的问题我。关于这本书,感谢您的提示,已经考虑过:)
  • @Alexey 正如@Mr.Wizard 所证明的那样,这种行为可以有成效的使用。众所周知,HoldAll 并不意味着该函数不评估参数,而只是表示该函数可以决定是否评估它。
  • @Alexey 我会说这是一个Head。我不会认为它是一种功能。当然,所有这些分类都是主观的。 Mathematica 不需要它,我们需要它。我认为这就是 mma 如此强大的原因 - 表达式是如此普遍,以至于它们可以用于如此不同的目的或它们的组合。
  • @Alexey $ConditionHold 似乎直截了当:坚持Condition。它确实(至少)这样做了:$ConditionHold[Print["!"] /; False] 我怀疑它被使用而不是简单地保留,以便可以选择性地解决或释放它。
  • @Mr.Wizard 我猜$ConditionHold 实际上只是一个HoldAll - 带有一个特殊名称的包装器(某些函数已知,如Cases 等),仅此而已.它似乎不是条件,而是满足条件时执行的代码。当然我可能是错的。
【解决方案2】:

Condition 的使用通常取决于左侧的内容,因此它必须至少在某种程度上评估 LHS。考虑:

MatchQ[3, a_ /; IntegerQ[a]]
p = {a_, b_};

MatchQ[{3, 0.2}, p /; IntegerQ[a] && b < 1]

对于这个和从这个,我会猜到Condition 具有属性HoldRest 而不是HoldAll。它可能需要HoldAll 用于某些内部使用,可能与SetDelayed 的用法有关。

【讨论】:

  • @Alexey,不是Cases 示例,因为RuleDelayed 而不是Condition 的Hold 属性?我认为“技巧”可能属于您说明的:=/;的特殊处理。
  • @Mr.Wizard 你是对的。 Unprotect[Condition]; ClearAttributes[Condition, HoldAll]; Cases[{3, 3.}, a_ :&gt; Print[a] /; IntegerQ[a]] 不会改变上述行为。似乎当Condition 放在RuleDelayedSetDelayed 的第二个参数内时,TagSetDelayedUpSetDelayed 的工作方式与默认情况完全不同。有趣的是,在 RuleDelayed 的情况下,它的工作方式不同,即使放在第一个参数内:Cases[{3, 3.}, (Print["!"]; a_) /; IntegerQ[a] :&gt; (Print[a] /; IntegerQ[a]; Print["!!"])]
  • @Alexey,/; 的位置确实不寻常,而且不同的人对放置位置有不同的约定。如果您清除 /; 的属性,然后尝试:Cases[{3, 3.}, a_ /; IntegerQ[a] :&gt; Print[a]] 您会看到它失败,因为 IntegerQ[a] 的计算结果为 False
  • 我在上面的评论中犯了一个错误:在RuleDelayed 的第一个参数内部Condition 的行为是相同的:第一个参数是第一次评估并且只有@987654352 @ 保留为 Condition 的第一个参数,以便将来从 Cases 调用 Condition。看来是因为Cases的内部优化。
猜你喜欢
  • 2012-05-15
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-04
  • 2011-08-10
  • 2011-01-19
  • 2012-02-20
相关资源
最近更新 更多