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

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

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

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

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

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

3 ответов


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

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

Out[1]= Function[x$, e94]

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

HTH!


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

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

такое утверждение, как: 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. Теперь это все возможно, потому что сопоставление шаблонов происходит с шаблоном[z, Blank[]] из первого блока кода. Это работает, даже если вы извращенно установили z в число. Иначе говоря.

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

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

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

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

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

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

выходы:

tmp[expr29]

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

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

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

Если правило не содержит местных переменная не переписывается:

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

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

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

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

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


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