Localization Mathematica中的词汇和动态范围:带模块、带和块的局部变量
以下代码返回14,正如您所期望的那样:Localization Mathematica中的词汇和动态范围:带模块、带和块的局部变量,localization,variables,module,scope,wolfram-mathematica,Localization,Variables,Module,Scope,Wolfram Mathematica,以下代码返回14,正如您所期望的那样: Block[{expr}, expr = 2 z; f[z_] = expr; f[7]] 但是如果您将块更改为模块,那么它将返回2*z。 除了本地化expr之外,其他变量似乎并不重要。 我想我理解Mathematica中的模块、块和With,但我无法解释这个例子中模块和块之间的行为差异 相关资源: 来自Mathematica文档 摘自 PS:感谢、、和对这一奇怪现象的追踪。 达沃拉克在此澄清并触及问题的核心: 您在这两个表达式上都使用
Block[{expr},
expr = 2 z;
f[z_] = expr;
f[7]]
但是如果您将块
更改为模块
,那么它将返回2*z
。
除了本地化expr
之外,其他变量似乎并不重要。
我想我理解Mathematica中的模块、块和With,但我无法解释这个例子中模块和块之间的行为差异
相关资源:
- 来自Mathematica文档
- 摘自
您在这两个表达式上都使用了跟踪吗?首先,我认为您暴露了一个错误 其次,我想我可以提供一些关于为什么会发生这种情况的见解,记住我对mathematica的内部知识是有限的 像:f[z_]:=2 z这样的完整形式的语句是:
SetDelayed[f[Pattern[z, Blank[]]], 2 z]
这会将下降值[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
仍然为f生成相同的下行值:
{HoldPattern[f[z_]] :> 2 z}
这是可能的,因为模式具有HoldFirst属性
如果在模块内对此进行求值,则HoldFirst属性不足以提供保护。
例如:
产出:
tmp[expr$8129]
我建议,因为HoldFirst属性不提供模块变量重写规则的豁免权,所以包含局部变量的规则中的任何模式都会重写其模式变量。符号->符号[SymbolName[sym]~“$”]
在简单的alpha转换中,在规则的两侧都重写了z
如果规则不包含局部变量,则不会进行重写:
Module[{expr},
Hold[z_ -> (z)]
]
(*Hold[z_ -> z]*)
应用上述总括规则,而不是搜索局部变量是否与规则变量匹配
因此,问题在于,在阿尔法转换发生之前,不会对局部表达式进行求值。或者,更好的方法是将expr包装在一个延迟评估的alpha转换中,这对于规则延迟来说是必需的
这不会发生在块中,因为块不会重写任何局部变量
还有其他想法吗?
有人看到我的逻辑有漏洞吗?我对此也有点惊讶,但我不认为这是一个错误。如果您深入查看中的示例,在标记为可能问题的部分下,有一个小注释说“变量在嵌套范围内重命名”,并给出以下示例:
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
函数
是另一个作用域构造,类似于模块
,因此x
在函数
的范围内被内部重命名为x$
,类似于您在跟踪
中发现的关于z
的内容
在模块
定义f
中,集
是另一个这样的作用域构造,因此当f
在模块
内定义时,而不是在块
内定义时,z
被重命名。按照模块
文档中该示例的建议,您可以从函数的各个部分构建RHS,以避免对嵌套范围进行词法重命名:
In[3]:= Clear[f, z]
In[4]:= Module[{expr},
expr = 2 z;
Set @@ {f[z_], expr};
f[7]]
Out[4]= 14
嗯 啊,聪明!因此,
z$
在模块版本中变成了z$
。所以我想这就解释了,但我还是不明白为什么会这样!也许我会更新这个问题,专门问一下这个奇怪的问题。非常感谢,达沃拉克。你和迈克尔·皮拉特似乎在讨论同一个问题,对吧?这是同一个问题。alpha转换提供了函数、规则、集合、SetDelayed的作用域,但重写的变量仍在全局名称空间中。我想我错了,我认为这是一个错误,但这是相当恼人的。哇,不是我所说的一个漂亮的工作,而是令人印象深刻的工作,找出了这一点!这就是为什么我每天都这样回来。向我学习了一些s**t:-)。我宁愿让expr包装在一个懒惰的alpha转换中,而不是处理不一致的语法,以承受意外的性能损失。这里要考虑的语言设计因素是什么?这项工作并不完美,因为集合的HoldFirst属性不再持有f[z_2;]。如果在未清除f的情况下对示例代码求值两次,则会出现set::write错误。很好地抓住了Michael Pilat.Davorak,对,但是您可以使用,例如Set@@Hold[f[z],expr]
来避免这个问题。或者,您可以使用f=Function@@@z,expr}
也许我们也需要开始使用Perl的TMTOWTDI for Mathematica=)我不能谈论子作用域重命名行为背后的确切考虑因素,除了f[z]=…
实际上是指希望将z
视为f
定义中的局部变量,而重命名(有时是不必要的)确保了这一点。
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
In[3]:= Clear[f, z]
In[4]:= Module[{expr},
expr = 2 z;
Set @@ {f[z_], expr};
f[7]]
Out[4]= 14