【问题标题】:Mathematica Module versus With or Block - Guideline, rule of thumb for usage?Mathematica Module 与 With 或 Block - 使用指南、经验法则?
【发布时间】:2011-10-03 10:25:12
【问题描述】:

Leonid 在他的书的第四章中写道:“... Module、Block 和 With。这些结构在 Mathematica Book 和 Mathematica Help 中有详细的解释,所以我在这里只说几句话。... "

从我读到的(能够找到)我仍然在黑暗中。对于打包的函数,我(简单地)使用模块,因为它有效并且我知道构造。虽然它可能不是最好的选择。我并不完全清楚(从文档中)何时、何地或为什么使用 With (或 Block )。

问题。是否有关于何时使用 Module、With 或 Block(对于包中的函数)的经验法则/指南?与模块相比有限制吗?文档说 With 更快。我希望能够为 Module(或其他构造)捍卫我的 =choice=。

【问题讨论】:

标签: wolfram-mathematica


【解决方案1】:

正如您所提到的,有很多事情需要考虑,并且可以进行详细讨论。但这里有一些我在大多数情况下都会应用的经验法则:

Module[{x}, ...] 是最安全的,如果有的话可能需要

  1. 您希望避免在模块评估期间破坏 x 的现有定义,或者

  2. 存在依赖于 x 未定义的现有代码(例如 Integrate[..., x] 之类的代码)。

Module 也是创建和返回新符号的唯一选择。出于这个原因,在高级动态编程中有时需要模块。

如果您确信 x 没有重要的现有定义或任何依赖于它的未定义代码,那么 Block[{x}, ...] 通常更快。 (请注意,在一个完全由您编写的项目中,对这些条件有信心是一个合理的“封装”标准,无论如何您都可能希望强制执行,因此在这些情况下,Block 通常是一个合理的选择。)

With[{x = ...}, expr] 是唯一在Hold[...] 中注入 x 值的作用域构造。这是有用且重要的。 With 可以比 Block 更快或更慢,具体取决于 expr 和采用的特定评估路径。但是,With 不太灵活,因为您不能在 expr 中更改 x 的定义。

【讨论】:

  • +1 一个很好的总结!我只有两个 cmets:首先,如果我们同意 RuleRuleDelayed 是作用域构造(它们在某些方面是),它们会提供另一种(非等效)方式来注入内部持有的表达式。其次,我不会使用Block 进行简单封装,除非完全需要它的动态范围功能 - 很难对执行堆栈的某些部分负责,特别是如果您将函数作为参数传递,可以执行任意代码的地方。现在使用Block 对速度的小幅提升可能会在以后导致非常细微的错误。
  • 在处理用户提供的函数或表达式时,使用 Block 封装不安全,这是一个很好的观点,因为它们可能引用 Global` 变量。在这种情况下,Block 只有在包的私有部分(即在Begin["`Private`"] 语句之后)才是安全的。
  • Unique[] 不是另一种创建和返回新符号的方法吗? Module[{x},x] 产生类似$x123 的东西,而Unique[] 产生类似$234 的东西。
  • 是 Unique[] 也可用于创建新符号。 Module[{x}...] 相对于 x=Unique[] 之类的优势在于,您将获得唯一的 x,例如设置语句,例如 x=5。在 x=Unique[] 之后,这样的语句将不起作用。你需要像 Evaluate[x]=5 这样的体操。
【解决方案2】:

BlockModule 之间更实际的区别可以在这里看到:

Module[{x}, x]
Block[{x}, x]
(*
-> x$1979
   x
*)

所以如果你想返回例如x,你可以使用Block。例如,

Plot[D[Sin[x], x], {x, 0, 10}]

不起作用;为了使它工作,人们可以使用

Plot[Block[{x}, D[Sin[x], x]], {x, 0, 10}]

(当然这并不理想,只是一个例子)。

另一种用法是Block[{$RecursionLimit = 1000},...],它会暂时更改$RecursionLimitModule 不会起作用,因为它重命名了$RecursionLimit)。

也可以使用Block 来阻止对某事的评估,例如

Block[{Sin}, Sin[.5]] // Trace
(*
-> {Block[{Sin},Sin[0.5]],Sin[0.5],0.479426}
*)

即,它返回Sin[0.5],仅在Block 完成执行后评估。这是因为Block 中的Sin 只是一个符号,而不是正弦函数。你甚至可以做类似的事情

Block[{Sin = Cos[#/4] &}, Sin[Pi]]
(*
-> 1/Sqrt[2]
*)

(使用Trace 看看它是如何工作的)。所以你也可以使用Block在本地重新定义内置函数:

Block[{Plus = Times}, 3 + 2]
(*
-> 6
*)

【讨论】:

  • 按照你的例子我试过了。清除[x]; x = 1; Module[{x}, x] - 它返回了(对我而言)意外结果 x$114。
  • 带有$RecursionLimit的例子非常有用。
  • 关于你的例子Plot。我相信Plot 有一些启发式方法来决定是否评估其参数之前一个数值被替换x 或仅在之后。您使用Block 的唯一区别在于Plot 何时评估其参数。它没有说明Block 的工作原理。您可以通过在Plot 的参数中包含Print[x] 语句来验证这一点。 (在Plot 中将未记录的Evaluated 选项设置为False 似乎不起作用。)
  • 我的意思是Plot 的问题在于对参数的评估。使其工作的正确方法是Plot[Evaluate[...], ...]。将参数包装在 Block 中也修复它的事实仅仅是偶然的,这是由于 Plot 的内部启发式来决定评估顺序。
  • @ndroock1 是什么让这出乎意料?这个想法是 Module 重命名其参数,因此它们不受 Module 正文之外的定义的影响
【解决方案3】:

安德鲁已经提供了一个非常全面的答案。我只想总结一下 Module 用于定义可以在函数定义范围内重新定义的本地 变量,而 With 用于定义本地 constants ,这是不可能的。您也不能根据在同一 With 语句中设置的另一个局部常量的定义来定义局部常量,或者在定义的 LHS 上有多个符号。也就是说,下面的方法不起作用。

With[{{a,b}= OptionValue /@ {opt1,opt2} }, ...]

我倾向于使用Module 括起With 来设置复杂的函数定义。我首先在With 中设置了所有本地常量,例如传递给函数的数据的Length,如果需要,则根据需要使用其他局部变量。原因是With 比你快一点,你确实有常量而不是变量。

【讨论】:

  • 实际上,(至少在v7中)你也不能根据Module中的其他变量来定义局部变量,至少在定义阶段,即Module[{a = 1, b = a}, b]返回aBlock[{a = 1, b = a}, b] 返回1。由于变量在With 中是不可变的,因此无法解决这个问题,但在Module 的主体中,您可以设置b = a 并使其行为类似于Block
  • 我倾向于在应该使用Function 的地方使用With,以提供变量名称。我这样做是为了确保纯函数表现得更好一些,但Function 可能更合适。
  • @rcollyer:是的,我的意思是我定义了局部变量 tha 依赖于 Module 的主体内和封闭的 With 内的另一个变量。对不起,如果这不清楚。如果我要在函数定义中多次使用它们,我会在 With 中使用命名常量。否则纯函数是要走的路。
【解决方案4】:

我想提一下有关BlockModule 之间区别的官方文档,请访问http://reference.wolfram.com/mathematica/tutorial/BlocksComparedWithModules.html

【讨论】:

  • 谢谢。 - 我认为你的文件索引比我好。我大部分时间都依赖谷歌。
猜你喜欢
  • 2011-09-08
  • 2011-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-30
  • 2012-08-15
  • 2011-11-03
  • 1970-01-01
相关资源
最近更新 更多