smalltalk - Block 在 Squeak 中使用外部变量

标签 smalltalk squeak

在一些 myClass 类中,我有:

foo
   |i b|
   i := 5.
   b := [i := i * 2. i].
   i :=3.
   ^b

我跑:

|a i b1 b2|
i := 4.
a := MyClass new.
b1 := a foo.
b2 := a foo.
Transcript show: b1 value; cr.
i := 1.
Transcript show: b1 value; cr.
Transcript show: b2 value; cr.

我不明白为什么输出是:

6
12
6

能否解释一下编译器是如何计算出来的?

最佳答案

您得到的是预期的行为。首先,脚本中定义的临时i,无论其名称如何,与#foo中的临时i没有任何影响或关系。方法,这样你就可以从脚本中删除它。

其次,该方法以 BlockClosure 进行响应,即 [i := i*2。我]。这意味着它将记住 i 的值,将其加倍,然后返回它。作为副作用,该方法还将 i 的值设置为 3

然后,当您第一次计算 b 时,i3 开始,加倍为 6 > 然后返回。因此,您得到6。第二次评估仅评估该 block ,因此它使用 i 的当前值(即 6),将其加倍并返回结果,即 12。再次计算 b,您将得到 2448 等。

另请注意,每次发送 #foo 时,它应答的 block 的计算结果将为 6

附录

问题提到了编译器。好吧,编译器在脚本中做的事情很少。然而,在#foo 中,它做了一些更复杂的事情,它使用代码[i := i * 2.i] 创建了BlockClosure。 block 中未使用的临时对象存在于堆栈中,但此临时 i 则不然。为什么?因为 block 是一个在方法执行后仍然存在的对象,并且一旦方法返回,堆栈就消失了。然后,该 block 将在 Array 中分配变量 i,该变量表示该 block 需要继续工作的环境。这个数组不是在堆栈中分配的,而是在对象内存中分配的。这是 block 记住i的最后一个值的地方,并且即使在调用#foo之后也可以根据需要多次更新和使用它,以及它的堆栈使用的内容会被其他调用覆盖。 (是的,在 Smalltalk 中,编译器也是第一类对象。)

关于smalltalk - Block 在 Squeak 中使用外部变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57979654/

相关文章:

Smalltalk - 是否可以通过方法将字符串添加到 String 实例?

string - 为什么这个 block 没有修剪字符串

smalltalk - SmalltalkHub与SqueakSource3

dom - 如何收集多个网页的 h1 标题?

smalltalk - 如何获取协议(protocol)中的所有方法?

github - 将一个项目从 squeak smalltalk 上传到我的 github 帐户

smalltalk - 在测试中检查类的文本表示?

smalltalk - 某些 WriteStream 消息背后的逻辑是什么?

smalltalk - 如何在 Pharo 中创建 MC 包

smalltalk - Smalltalk 实例变量名称和方法中允许使用哪些特殊字符?