在某些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
是否可以解释编译器如何解决?
答案 0 :(得分:4)
您得到的是预期的行为。首先,脚本中定义的临时i
与i
方法中的临时#foo
都没有任何影响或关系,无论其名称如何,都可以从中摆脱掉脚本。
第二,该方法用BlockClosure
来回答,即[i := i*2. i]
。这意味着它将记住i
的值,将其加倍,然后返回。副作用是,该方法还将i
的值设置为3
。
然后,当您第一次评估b
时,i
从3
开始,被加倍为6
,然后返回。因此,您得到6
。第二个评估只是评估块,因此它使用i
的当前值6
,将其加倍并返回结果12
。再次评估b
,您将得到24
,48
等。
还请注意,每次您发送#foo
时,它回答的阻止都将评估为6
。
该问题提到了编译器。好吧,编译器在脚本中做的很少。但是,在#foo
中,它做了一些更复杂的事情,它使用代码BlockClosure
创建了[i := i * 2. i]
。不在块中使用的临时文件位于堆栈中,但此临时i
则没有。为什么?因为该块是一个对象,该对象将在方法执行后幸免,并且方法返回后堆栈就消失了。然后,该块将在i
中分配变量Array
,该变量表示该块需要继续工作的环境。 Array
未分配在堆栈中,而是分配在对象存储器中。这是该块记住i
的最后一个值的地方,甚至可以在调用#foo
之后根据需要多次更新和使用它,并且所使用的堆栈也会被其他调用覆盖。 (是的,在Smalltalk中,编译器也是一流的对象。)