【问题标题】:How does Return work?退货如何运作?
【发布时间】:2011-12-16 12:43:37
【问题描述】:

我并不完全清楚 Return[] 的工作原理。

文档说:

Return[expr] 从函数返回值 expr。

但在 Mathematica 中并不清楚使用模式和Set(Delayed) 定义的“函数”的边界在哪里。系统只是应用替换规则,直到不再需要更改为止。

考虑以下示例:

a := (Module[{}, Return[0]; 1]; 2)    
a
(* 0 *)

我并不完全清楚为什么Return 是从a 爆发出来的,而不是从ModuleCompoundExpression 之一爆发出来的。

以下不起作用,只返回Return 表达式。它让我想起了Unevaluated 的处理方式。

(Module[{}, Return[0]; 1]; 2)
(* Return[0] *)

如果我们再增加一层定义,Return的效果就到此为止了:

b := (a; 3)
(* 3 *)

为什么这三个输入会给出不同的结果?决定Return 准确突破的一般规则是什么?

答案在于完全理解评估过程。

是否有人愿意阐明这一点,也许可以通过指向文档中明确这一点的部分的指针来说明这一点?

【问题讨论】:

  • 注意Return的行为有changed from one version to another.我自己一般尽量避免使用。
  • @Mr.W 哇,如果它的行为即使在次要版本之间也会发生变化,那么确实最好避免!
  • @Mr.Wizard, Szabolcs 就我个人而言,我不认为这个特别的观察是反对使用Return 的有力证据,因为它只是新功能的添加 ( Fold),并且没有旧代码被破坏(另请参阅我的回答下方的评论)。
  • @Leonid 我似乎还记得至少另一种情况,我想在 Mathgroup 上,每个版本的 Return 行为不同。这听起来你很陌生吗?另外,我没有考虑太多,但改变不能重新:折叠破坏现有的 v7 代码吗?这可能是 Return 的一种奇怪用法,但不会像可变的 PatternTest 函数那样奇怪...... ;-)
  • @Mr.Wizard 好吧,我想一个 可以 构造一个在新版本中会“损坏”的代码示例,但我很难想象它会比你提到的方法更不反常(实际上,我确实建设性地使用了几次)。

标签: function-construction evaluation core-language faq


【解决方案1】:

已在this 线程中给出了详尽的答案(如果您在那里结合了多个答案。所提到的线程也是一般推荐的阅读内容,以了解有关此的更多信息 - 至少我知道一个)。我将首先在这里复制我从那里得到的正确答案。

Return 中包裹的任何表达式都有两种可能的结果:要么是在定义了 Return 动作的某个词法(或动态)范围构造内 - 然后 Return 的存在将导致打破范围构造过程,或者它不是,然后它只是一个符号表达式,就像任何其他的一样。在脱离作用域构造后(或者如果它不在任何作用域构造内,则仅进行评估),Return 只有在从 r.h.s. 内调用时才会被丢弃。用户定义的规则。这是一个例子:

In[1]:= 
Clear[a,b,c]; 
c=(Return[a];3) 

Out[1]= Return[a] 

In[2]:= b:=(Return[a];3) 
In[3]:= b 
Out[3]= a 

可以通过查阅评估程序的确切规则来解释这种行为。由于缺乏更新的说明,我在此引用 David Withoff 1992 年的“Mathematica internals”:评估循环的最后一步是(第 3 章 - 评估,第 7 页,底部):“丢弃头部Return,如果存在,用于通过应用用户定义的规则生成的表达式。"

因此,当您使用SetDelayed 时,您创建了一个用户定义的延迟规则,然后Return 被丢弃,而对于像“直接”评估这样的

In[4]:= Return[a] 
Out[4]= Return[a] 

它不是。但是,在

In[5]:= 
ClearAll[a,c];
a:=Return[c];
a

Out[7]= c 

它是,并且Return 被丢弃,即使没有范围构造可以突破。在您的示例中也会发生同样的情况,其中Module 被环绕-Return 可以从Module 中分离出来,但不会被丢弃,因为Module 不是任何用户定义规则的r.h.s。

以下是我所知道的关于 Return 如何工作的最佳描述,这要归功于 Alan Hayes(可以在 this MathGroup 线程中找到):

“鉴于最近关于在第二个参数列表中保留参数的线程 Return 上的以下注释可能会引起您的兴趣:

  • 如果 Return[x] 生成为 DoScan 中的值,则 x 立即 返回;

  • 如果Return[x] 生成为CompoundExpression 中的条目或值 WhileFor 循环的主体,然后 Return[x](不是 x)立即 回来;

  • 如果 Return[x] 作为用户定义函数的值生成,则 函数返回x(不是Return[x]

  • 否则Return[x] 表现为普通表达式。 "

这些“经验”规则与 Withoff 报告中的陈述一致,后者使前者听起来不那么神奇。

记住Return 有第二个可选参数也很好,它告诉它从哪个周围的作用域构造突破:

In[242]:= (Module[{}, Return[0, CompoundExpression]]; 2)
Out[242]= 0

但是

In[243]:= (Module[{}, (Return[0, CompoundExpression]; 0)]; 2)
Out[243]= 2

在您的最后一个示例中,b 中定义的外部 CompoundExpression 动态而不是词法范围(相对于a 中定义的内容),因此这不起作用(换句话说,您的最后一个结果也符合预期)。

【讨论】:

  • 让我看看我是否做对了:1.Module/Block/etc。 修改Return 的处理(即它们与CompoundExpression 相同:CompoundExpression 的其余部分被忽略并立即返回Return[value])。 2. DoScan 的行为特别,因为它们返回 value 而不是 Return[value]。 3.Return[value]如果是延迟型替换规则(:=)应用的结果,总是被value替换,但不是在普通替换规则的情况下(=).. .
  • ... 另一个我没想到的有趣点是:== 之间的区别。试试a := Return[0]b = Return[0]。两者的行为不同,但它们的 OwnValues 看起来完全相同相同(都使用 :>)。 Information 确实向我们展示了是否在定义中使用了 :==。我不知道:== 的处理存在差异,即使定义之后...
  • @Szabolcs 您的 cmets 确实表明我没有在我的答案中付出足够的努力,这是真的 - 我主要编译它我知道的来源:)。好的,对于您的第一条评论:所有正确,但直接分配的原因不是这 不是 替换规则,而是它的 r.h.s.被立即评估并且那个评估不是由现有的替换规则引起的。考虑一下,例如:ClearAll[a, b, c];a = b;b = Return[c];a - 最后你会得到简单的 c,即使所有规则都是即时的 - 但这里来自 bReturn[c] 评估规则的结果对于a
  • 是的,你是对的。出于某种原因,我认为在a = Return[0] 评估a 之后会返回Return[0],但事实并非如此。它返回0。所以在定义之后:==之间没有区别。 Return 上的更多 cmets: 1. f[Return[0,f]] 的行为方式与 Module[{}, Return[0,Module]] 不同(或 Fold 或显然任何其他可能有意义的内置函数)。 Return 的第二个参数虽然没有记录在 M8 中,所以它可能已被弃用。 2. 请注意Return 将从Dialog[] 返回
  • @Szabolcs 好的,所以:首先,带有第二个参数的Return 似乎只适用于某些封闭函数,而不仅仅是任何任意的f。其次,虽然没有记录带有两个参数的Return,但它不太可能消失并且是“半官方”认可的(参见例如stackoverflow.com/questions/7446032/…),所以我认为它可以安全使用。下一个:您构建的案例:x = Module[{}, Module[{}, While[True, Return[0]]; 1]; 2] - 这里 似乎 ReturnModule 中断(确实如此),但机制是..
猜你喜欢
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 2015-05-17
  • 2011-08-15
  • 2023-03-12
  • 1970-01-01
  • 2013-07-13
  • 2013-01-05
相关资源
最近更新 更多