Насколько я могу судить (а об этом уже упоминали другие авторы), Condition
следует рассматривать не как отдельную функцию, а как обертку, используемую при формировании больших выражений, включающих шаблоны.Но я хочу подчеркнуть, что часть этой тонкости здесь заключается в том, что Rule
и RuleDelayed
являются ограничивающими конструкциями.Вообще, у областей видимости должны быть стадии привязки переменных, где они разрешают возможные конфликты в именах переменных и фактически связывают переменные с их вхождениями в теле конструкции объема (или в правой части правила для 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}}
То, что вы видите, это то, что есть специальные внутренние головки 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
), и следует учитывать этап переменной привязки в ограничивающих конструкциях, когда анализируя их поведение.