Лексическая и динамическая область видимости в Mathematica: локальные переменные с Module, With и Block

Следующий код возвращает 14, как и следовало ожидать:

Block[{expr},
  expr = 2 z;
  f[z_] = expr;
  f[7]]

Но если вы измените это Block на Module, оно вернет 2*z. Кажется, не имеет значения, какие другие переменные, кроме expr, вы локализуете. Я думал, что понял модуль, блок и с в Mathematica, но я не могу объяснить разницу в поведении между модулем и блоком в этом примере.

Связанные ресурсы:

PS: благодаря Майклу Пилату, Davorak и Bill White за запах- следим за этой странностью. Даворак разъясняет и доходит до сути проблемы здесь: Mathematica нарушает обычные правила области видимости в модуле?


person dreeves    schedule 28.04.2010    source источник


Ответы (3)


Я тоже был немного удивлен этим, но я не думаю, что это ошибка. Если внимательно изучить примеры на справочной странице для Module< /a> в разделе Возможные проблемы есть небольшое примечание, в котором говорится "Переменные переименовываются во вложенных областях" и приводится следующий пример:

In[1]:= Module[{e = Expand[(1 + x)^5]}, Function[x, e]]

Out[1]= Function[x$, e$1194]

In[2]:= %[10]

Out[2]= 1 + 5 x + 10 x^2 + 10 x^3 + 5 x^4 + x^5 

Function — это еще одна конструкция области видимости, такая как Module, поэтому x внутренне переименована в x$ в области действия Function, аналогично тому, что вы обнаружили с помощью Trace о z.

В вашем Module, определяющем f, Set является еще одной такой конструкцией области видимости, и поэтому z переименовывается, когда f определяется внутри Module, но не когда он находится внутри Block. Следуя совету этого примера из документации Module, вы можете построить RHS вашей функции из ее частей, чтобы избежать лексического переименования вложенной области видимости:

In[3]:= Clear[f, z]

In[4]:= Module[{expr},
  expr = 2 z;
  Set @@ {f[z_], expr};
  f[7]]

Out[4]= 14

ХТХ!

person Michael Pilat    schedule 29.04.2010
comment
Вау, не то, что я бы назвал красивым обходным путем, но впечатляющая работа по выяснению этого! - person dreeves; 29.04.2010
comment
Вот почему я возвращаюсь к SO каждый день. Учит меня кое-чему дерьму :-). - person Timo; 29.04.2010
comment
Я предпочитаю неожиданный удар по производительности, связанный с ленивым альфа-преобразованием, вместо того, чтобы иметь дело с непоследовательным синтаксисом. Какие соображения по языковому дизайну здесь играют роль? - person Davorak; 29.04.2010
comment
Этот обходной путь не идеален, так как f[z_] больше не удерживается атрибутом Set's HoldFirst. Вы получите ошибку set::write, если дважды оцените код примера, не очищая f. Хороший улов Михаэля Пилата. - person Davorak; 29.04.2010
comment
Davorak, да, но вы могли бы использовать вместо этого, например, Set @@ Hold[f[z_], expr], чтобы избежать этой проблемы. Или вы могли бы использовать f = Function@@{z, expr} Возможно, нам нужно начать использовать TMTOWTDI Perl и для Mathematica =) Я не могу говорить о точных соображениях, лежащих в основе поведения переименования подобласти, кроме того, что f[z_] = ... фактически говорит, что вы хотите рассматривать z как локальная переменная внутри определения f, и переименование, хотя иногда и ненужное, гарантирует это. - person Michael Pilat; 29.04.2010

Во-первых, я думаю, что вы выявили здесь ошибку.

Во-вторых, я думаю, что могу дать некоторое представление о том, почему это происходит, имея в виду, что мои знания о внутренностях математики ограничены.

Утверждение типа: f[z_] := 2 z в полной форме:

SetDelayed[f[Pattern[z, Blank[]]], 2 z]

Это устанавливает DownValue[f] в:

{HoldPattern[f[z_]] :> 2 z}

Затем позже, когда выражение, такое как f[2], оценивается позже, выполняется что-то вроде следующего:

f[2] /. HoldPattern[f[z_]] :> 2 z

Что будет оцениваться как 4. Теперь все это возможно, потому что сопоставление с образцом происходит с Pattern[z, Blank[]] из первого блока кода. Это работает, даже если вы ранее установили z на число. Другими словами.

z = 5;
f[z_] := 2*z

По-прежнему производит те же значения downvalue для f:

{HoldPattern[f[z_]] :> 2 z}

Это возможно, потому что Pattern имеет атрибут HoldFirst.

Атрибут HoldFirst не является достаточной защитой, если вы оцениваете его внутри модуля. Пример:

SetAttributes[tmp, HoldFirst];
Module[{expr},
 expr = 2 z;
 tmp[expr]
]

выходы:

tmp[expr$8129]

Я предлагаю, поскольку атрибут HoldFirst не обеспечивает иммунитет к правилу перезаписи переменных модуля, чтобы любой шаблон в правиле, содержащем локальную переменную, переписывал свои переменные шаблона. sym->Symbol[SymbolName[sym]~~"$"]

Module[{expr},
 Hold[z_ -> (z; expr)]
]
(*Hold[z$_ -> (z$; expr$1391)]*)

z был переписан с обеих сторон правила в простом альфа-преобразовании.

Если правило не содержит локальной переменной, перезаписи не происходит:

Module[{expr},
 Hold[z_ -> (z)]
]
(*Hold[z_ -> z]*)

Вместо того, чтобы искать, соответствует ли локальная переменная переменной правила, применяется вышеуказанное общее правило.

Итак, проблема в том, что локальное выражение не оценивается до того, как произойдет альфа-преобразование. Или, возможно, даже лучше было бы иметь expr, обернутое в лениво оцениваемое альфа-преобразование, которое потребуется для RuleDelayed.

В Block этого не происходит, потому что Block не перезаписывает ни одну из локальных переменных.

Любые другие идеи? Кто-нибудь видит дыры в моей логике?

person Davorak    schedule 29.04.2010
comment
Большое спасибо, Даворак. Кажется, вы и Майкл Пилат пришли к одному и тому же вопросу, верно? - person dreeves; 29.04.2010
comment
Это та же проблема. Альфа-преобразование дает область действия для Function, Rule, Set, SetDelayed, но переписанные переменные все еще находятся в глобальном пространстве имен. Наверное, я ошибался, говоря, что это ошибка, но это довольно раздражает. - person Davorak; 29.04.2010

Вы использовали Trace для обоих выражений?

person Bill White    schedule 28.04.2010
comment
Ах, умница! Таким образом, z_ превращается в z$_ в версии модуля. Так что я думаю, это объясняет это, но я до сих пор не понимаю, почему это происходит! Может быть, я обновлю вопрос, чтобы спросить конкретно об этой странности. - person dreeves; 29.04.2010