块在Squeak中使用外部变量

时间:2019-09-17 17:55:17

标签: 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

是否可以解释编译器如何解决?

1 个答案:

答案 0 :(得分:4)

您得到的是预期的行为。首先,脚本中定义的临时ii方法中的临时#foo都没有任何影响或关系,无论其名称如何,都可以从中摆脱掉脚本。

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

然后,当您第一次评估b时,i3开始,被加倍为6,然后返回。因此,您得到6。第二个评估只是评估块,因此它使用i的当前值6,将其加倍并返回结果12。再次评估b,您将得到2448等。

还请注意,每次您发送#foo时,它回答的阻止都将评估为6

附录

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