为什么 Mathematica 会打破模块中正常的作用域规则?

发布于 2024-08-31 06:34:32 字数 1095 浏览 5 评论 0原文

正如最近的 范围界定在模块内部无法按预期工作。

该线程的一个示例是:

Module[{expr},
 expr = 2 z;
  f[z_] = expr;
  f[7]]
(*2 z*)

但以下内容几乎按预期工作。

Module[{expr},
 expr = 2 z;
  Set@@{f[z_], expr};
  f[7]]
(*14*)

是什么语言设计考虑使 Wolfram 选择此功能?

编辑:请参阅 Jefromi 的第一条评论,我将 z 从局部变量更改为非局部变量,但忘记更改输出。它不会影响问题。

编辑2: Michael Pilat 的观点似乎是 Block 和 Module 具有不同的功能。我想我理解他的观点,但我认为这与我的问题正交。所以这里有一个更新。

我可以在笔记本中的全局级别使用以下代码:

expr = 2 z;
f[z_] = expr;
f[7]
(*output: 14*)

但是当我将相同的代码块放入模块并使 expr 本地化时,它会产生不同的输出。

Clear[f];
Module[{expr},
 expr = 2 z;
 f[z_] = expr;
 f[7]]
(*output: 2z*)

如果跟踪上面的 Module 调用,您会发现 Set[f[z_], expr] 被重写为 Set[f[z$_,expr]。现在,这个 z->z$ 变换同时发生在集合的左轴和右轴上。然而,它发生在对 expr 求值之前,这会导致在全局级别获得不同的结果。

z->z$ 转换似乎仅在 rhs 具有模块调用本地符号时才会发生。

为什么 Mathematica 选择在模块调用中更改此语法?这里存在哪些语言/实现设计权衡来做出这个决定。

As was pointed out in a recent post scoping does not work as expected inside of Module.

An example from that thread is:

Module[{expr},
 expr = 2 z;
  f[z_] = expr;
  f[7]]
(*2 z*)

But the following works almost as expected.

Module[{expr},
 expr = 2 z;
  Set@@{f[z_], expr};
  f[7]]
(*14*)

What language design consideration made wolfram choose this functionality?

Edit: See Jefromi's first comment I changed z from being a local variable to not and forgot to change the output. It does not effect the problem.

Edit2:
Michael Pilat's point seems to be that Block and Module have different functions. I think I understand his point, but I think that it is orthogonal to my question. So here is an update.

I can use the following code at the the global level in a notebook:

expr = 2 z;
f[z_] = expr;
f[7]
(*output: 14*)

But when I put the same code block into a Module and make expr local it produces a different output.

Clear[f];
Module[{expr},
 expr = 2 z;
 f[z_] = expr;
 f[7]]
(*output: 2z*)

If you trace the above Module call you find that Set[f[z_], expr] is rewritten to Set[f[z$_,expr]. Now this z->z$ transformation happens on both the lhs and rhs of the Set. It however happens before expr is evaluated, which causes a different result then would be obtained at the global level.

The transformation z->z$ only seems to happen when the rhs has a symbol local to the Module call.

Why does Mathematica choose to have this syntax change in a Module call? What language/implementation design tradeoffs exist here that made this decision.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

葬シ愛 2024-09-07 06:34:33

我认为答案非常简单,但很微妙:Module 是一个词法作用域构造,而Block 是一个动态作用域构造。

文档中的块与模块比较教程讨论了区别:

当使用词法作用域时,变量被视为程序中特定代码部分的局部变量。在动态作用域中,变量的值是程序执行历史的一部分的局部值。
在 C 和 Java 等编译语言中,“代码”和“执行历史记录”之间有非常明显的区别。 Mathematica 的符号性质使得这种区别稍微不太清楚,因为“代码”原则上可以在程序执行期间动态构建。

Module[vars, body] 的作用是将模块执行时表达式主体的形式视为 Mathematica 程序的“代码”。然后,当任何变量明确出现在这个“代码”中时,它被认为是本地的。
Block[vars, body] 不查看表达式主体的形式。相反,在对 body 进行评估的过程中,该块使用变量的本地值。

它提供了这个简化的示例:

In[1]:= m = i^2

Out[1]= i^2

(* The local value for i in the block is used throughout the evaluation of i+m. *)
In[2]:= Block[{i = a}, i + m]

Out[2]= a + a^2

(* Here only the i that appears explicitly in i+m is treated as a local variable. *)
In[3]:= Module[{i = a}, i + m]

Out[3]= a + i^2

也许关键点是要认识到 Module 用本地化版本替换模块主体中 i 的所有实例(例如,i$1234) 词法上模块的任何主体被实际评估之前。

因此,实际计算的模块主体是 i$1234 + m,然后是 i$1234 + i^2,然后是 a + i^2

没有任何问题,BlockModule 旨在表现不同。

I think the answer is pretty simple, but subtle: Module is a lexical scoping construct, and Block is a dynamic scoping construct.

The Blocks Compared With Modules tutorial from the documentation discusses the distinction:

When lexical scoping is used, variables are treated as local to a particular section of the code in a program. In dynamic scoping, the values of variables are local to a part of the execution history of the program.
In compiled languages like C and Java, there is a very clear distinction between "code" and "execution history". The symbolic nature of Mathematica makes this distinction slightly less clear, since "code" can in principle be built up dynamically during the execution of a program.

What Module[vars, body] does is to treat the form of the expression body at the time when the module is executed as the "code" of a Mathematica program. Then when any of the vars explicitly appears in this "code", it is considered to be local.
Block[vars, body] does not look at the form of the expression body. Instead, throughout the evaluation of body, the block uses local values for the vars.

It offers this reduced example:

In[1]:= m = i^2

Out[1]= i^2

(* The local value for i in the block is used throughout the evaluation of i+m. *)
In[2]:= Block[{i = a}, i + m]

Out[2]= a + a^2

(* Here only the i that appears explicitly in i+m is treated as a local variable. *)
In[3]:= Module[{i = a}, i + m]

Out[3]= a + i^2

Perhaps the key point is to realize that Module replaces all instances of i in the module body with a localized version (e.g., i$1234) lexically, before any of the body of the module is actually evaluated.

Thus, the module body that's actually evaluated is i$1234 + m, then i$1234 + i^2, then a + i^2.

Nothing is broken, Block and Module are intended to behave differently.

泛泛之交 2024-09-07 06:34:33

根据文档模块具有属性HoldAll,这会导致 Module 内的所有内容保持未计算状态,因此您的 exprexpr 之前不会计算为 2 z code> 被分配给 f[z_]

将第二个参数包装到 Evaluate 中的 Module 似乎可以解决问题:

In[1]:= Module[{expr}, Evaluate[expr = 2 z;
  f[z_] = expr;
  f[7]]]

Out[1]= 14

此外,使用 Block 而不是 Module 也可以:

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

Out[2]= 14

According to the documentation, Module has attribute HoldAll, which causes everything inside the Module to remain in an unevaluated state, so your expr is not evaluated to 2 z before expr is assigned to f[z_].

Wrapping the second argument to Module in Evaluate seems to solve the problem:

In[1]:= Module[{expr}, Evaluate[expr = 2 z;
  f[z_] = expr;
  f[7]]]

Out[1]= 14

Also, using Block instead of Module works:

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

Out[2]= 14
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文