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
应该下一行强调它。我喜欢莱安德罗解释的东西,通常没有什么可以添加或删除。