Smalltalk:上下文无法返回

KKK*_*oo0 5 closures smalltalk block

如果我逐个执行它们,以下Smalltalk代码将返回错误"context not not return".有人有解释吗?

  f := [ :x :y | ^x + y].
  sum:= f value: 3 value: 6.
Run Code Online (Sandbox Code Playgroud)

如果我一次执行它们,它会工作并按9预期返回.

Tob*_*ias 8

你可能想写

f := [ :x :y | x + y].
sum:=f value: 3 value: 6.
Run Code Online (Sandbox Code Playgroud)

为什么?

我来告诉你

非本地回报.

如果要从方法返回某些内容,可以在方法中使用^插入符来执行此操作:

doSomethingUseful
    ^ self calculate + 1
Run Code Online (Sandbox Code Playgroud)

这是正常的回报.一切还好.现在输入块.

总是隐含地从块中返回一些东西,它的最后一个表达式的值.因此,42执行时,此块将返回:

[1 + someObject invert.
 anotherObject * 4.
 42].
Run Code Online (Sandbox Code Playgroud)

您可以在方法中使用它:

doSomethingUseful: someObject to: anotherObject
    | myValue |
    myValue := [1 + someObject invert.
                anotherObject * 4.
                42] value.
    ^ self calculate + myValue
Run Code Online (Sandbox Code Playgroud)

但是,有时你必须从块中返回函数.一个典型的例子是这样的保护条款:

doThis
    self someValueSatisfied ifFalse: [^ self]
    self calculate.
    ^ self someValueComputed.
Run Code Online (Sandbox Code Playgroud)

如果#someValueSatsified返回false,则该方法立即返回并且从不执​​行#calculate#someValueComputed.这种效应被称为非局部返回,因为你从块返回给其调用上下文(这将是本地的),而是形成它在定义的方法(!).

为什么一气呵成?

这是由于处理"Do-it"的方式,例如.吱吱声或Pharo.当你按下Ctrl-D(或等效的)时,当前选择的代码被秘密编译为一个方法(好吧,发生了一点点,但让我们忽略它).如果执行1 halt并查看调试器,则可以看到.

因此,执行代码行将执行以下操作:

DoIt
    f := [ :x :y |    ^(x+y). ]. "! f is now defined in the work space"

DoIt
    sum:=f value: 3 value: 6.
Run Code Online (Sandbox Code Playgroud)

首先,创建块并将其存储在工作区中的某个位置f.然后这个do-it退出,下一个执行.Smalltalk f在您的工作区中查找,即存储块.它试图执行它并遇到非本地返回.但是,非本地返回仅从定义函数返回,该函数不再执行,因此我们无法从中返回.

当你一次性执行所有事情时,这将是:

DoIt
    f := [ :x :y |    ^(x+y). ].
    sum:=f value: 3 value: 6.
Run Code Online (Sandbox Code Playgroud)

几乎与上面相同,除了现在f执行时,非本地返回可以工作,因为我们仍然在定义块的函数中.所以我们可以从中回来.在这种情况下,非本地返回工作.