Вопрос по условию (/;) - PullRequest
10 голосов
/ 03 мая 2011

Condition имеет атрибут HoldAll, который запрещает оценку его первого аргумента перед применением Condition.Но по какой-то причине Condition оценивает свой первый аргумент, даже если тест дает False:

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

Почему это так?Для каких целей Condition оценивает свой первый аргумент, если тест дает False?В каких случаях это поведение может быть полезным?

PS Его поведение отличается, когда Condition используется в качестве второго аргумента SetDelayed:

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

Это то, что я ожидал длявсе дела.

Ответы [ 2 ]

6 голосов
/ 03 мая 2011

Насколько я могу судить (а об этом уже упоминали другие авторы), Condition следует рассматривать не как отдельную функцию, а как обертку, используемую при формировании больших выражений, включающих шаблоны.Но я хочу подчеркнуть, что часть этой тонкости здесь заключается в том, что Rule и RuleDelayed являются ограничивающими конструкциями.Вообще, у областей видимости должны быть стадии привязки переменных, где они разрешают возможные конфликты в именах переменных и фактически связывают переменные с их вхождениями в теле конструкции объема (или в правой части правила для Rule и RuleDelayed).Это может считаться частью внутренней работы конструкций определения объема, но, поскольку Mathematica допускает манипуляции на верхнем уровне с помощью атрибутов и таких вещей, как Evaluate, конструкции определения объема не так черны, как может показаться - мы можем изменитьпривязки, заставляя объявления переменных, или тело, или оба, вычислять до того, как привязка произойдет - например, удаляя некоторые атрибуты Hold* -.Я обсуждал эти вещи здесь более подробно, хотя, не зная точных деталей реализации для областей видимости, мне пришлось в основном догадываться.

Возвращаясь к случаю RuleRuleDelayed и 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 в качестве отдельной функции, отличается - таким образом, разница в результатах.

Мне известен один хороший пример, который очень хорошо иллюстрирует вышеизложенные моменты, в этот поток , где возможны реализации версии 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

Если бы все работало правильно, мы должны были получить 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 (оно съедает любое количество Unevaluated упаковщиков вокруг r.h.s.), отмеченным в комментариях к к этому вопросу .

Подводя итог: одно из наиболее часто предполагаемых применений Condition тесно связано с включающими ограничивающими конструкциями (Rule и RuleDelayed), и следует учитывать этап переменной привязки в ограничивающих конструкциях, когда анализируя их поведение.

4 голосов
/ 03 мая 2011

Condition использование часто зависит от того, что находится слева, поэтому оно должно оценивать LHS хотя бы в некоторой степени.Рассмотрим:

MatchQ[3, a_ /; IntegerQ[a]]
<b>   True</b>
p = {a_, b_};

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

И для этого, и для этого я бы предположил, что Condition имеет атрибут HoldRest, а не HoldAll.Вероятно, для внутреннего использования требуется HoldAll, возможно, связанный с использованием SetDelayed.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...