Smalltalk 块在Squeak中使用外部变量

Smalltalk 块在Squeak中使用外部变量,smalltalk,squeak,Smalltalk,Squeak,在一些myClassclass中,我有: 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 valu

在一些
myClass
class中,我有:

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
没有任何影响或关系,因此可以从脚本中删除它

其次,该方法以
块闭包
回答,即
[i:=i*2.i]
。这意味着它将记住
i
的值,将其加倍,然后返回它。作为副作用,该方法还将
i
的值设置为
3

然后,当您第一次计算
b
时,
i
3
开始,加倍到
6
,然后返回。因此,您将得到
6
。第二次求值只是对块求值,因此它使用当前值
i
,即
6
,将其加倍,并用结果回答,即
12
。再次计算
b
,您将得到
24
48
,等等

还请注意,每次发送
#foo
,它所回答的块将计算为
6

附录
这个问题涉及到编译器。嗯,编译器在脚本中做的很少。然而,在
#foo
中,它做了一些更复杂的事情,用code
[i:=i*2.i]
创建
块闭包。块中未使用的临时变量存在于堆栈中,但此临时
i
不存在。为什么?因为块是一个在方法执行后仍然存在的对象,而堆栈在方法返回后立即消失。然后块将在表示块需要保持工作的环境的
数组中分配变量
i
。此
数组
不是在堆栈中分配的,而是在对象内存中分配的。这是块记住
i
的最后一个值的地方,即使在调用
#foo
之后,块也可以根据需要多次更新和使用它,以及它使用的堆栈被其他调用写入。(是的,在Smalltalk中,编译器也是第一类对象。)

知道是什么吗
i:=5
没有任何区别,因为您仍然重新分配了
i
<代码>i
b
是临时变量。它们的作用域是方法调用。因此,每次调用一个方法时,
i
都是不同的(它不是某种实例变量或具有全局范围的其他东西),我甚至没有注意脚本中的
i
s:)@Uko我认为代码应该写得不同,
i
应该下一行强调它。我喜欢莱安德罗解释的东西,通常没有什么可以添加或删除。