Лексическая и динамическая область видимости в Mathematica: локальные переменные с Module, With и Block - PullRequest
7 голосов
/ 29 апреля 2010

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

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

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

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

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

Ответы [ 3 ]

6 голосов
/ 29 апреля 2010

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

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. 1018 *

В вашем 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

НТН!

3 голосов
/ 29 апреля 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, Пробел []] из первого блока кода.Это работает, даже если вы очевидно установили z на число.Другими словами.

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

Тем не менее выдает те же значения понижений для 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 не оценивается до того, как произойдет альфа-преобразование.Или, возможно, еще лучше было бы заключить expr в лениво вычисленное альфа-преобразование, которое потребуется для RuleDelayed.

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

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

2 голосов
/ 29 апреля 2010

Вы использовали трассировку в обоих выражениях?

...