为什么此代码将变量 a 设置为 9?

Why does this code set the variable a to 9?

我被这段代码搞糊涂了:

| a b c| a := 1. b := [a := a + 1]. c := [a := a - 2. b].
        10 timesRepeat: (a even ifTrue: b ifFalse: c). a

我的假设是这段代码会将 a 设置为 -19。每次迭代都会测试 a 是否为偶数,但 a 是否为奇数,因此将调用 c,从 a 中减去 2 而不影响其奇偶校验。 c 不会调用 b 因为,如果我对块的理解是正确的,块的最后一个元素是 returned 而不是求值;所以 c 会 return b,但是 timesRepeat 会丢弃任何 returned 的东西所以 c 中的 b 没有效果.

我的假设被证明是错误的:这段代码将 a 设置为 9。为了看看发生了什么,我稍微修改了这段代码:

| a b c| a := 1. b := [Transcript show: (a displayString). a := a + 1]. c := [Transcript  show: (a displayString). a := a - 2. b.].
           10 timesRepeat: (a even ifTrue: b ifFalse: c). a

这是打印的内容:

1-1012345678

所以看起来 b 调用的吗? b 是 returned 而不是调用是我的假设错误吗?

让我们尝试检查一下:

jkl := [Transcript show: 'I am called too.'].
asdf := [Transcript show: 'I am called!'. jkl].

10 timesRepeat: asdf

不,asdf 不会在此处调用 jkl

I am called!I am called!I am called!I am called!I am called!I am called!I am called!I am called!I am called!I am called!

而且无论如何,如果 c 总是调用 b,它的效果将是有效地从 a 中减去 1;但这不会发生。相反,第一次迭代似乎调用 c 然后,神秘地,每次迭代似乎调用 b,即使 a 是奇数!

这是怎么回事??

timesRepeat: 选择器需要一个块作为参数。您使用括号内的表达式调用它:

10 timesRepeat: (a even ifTrue: b ifFalse: c).

不过,碰巧 c 被定义为块 [a := a - 2. b],其中 return 是 b 的值,而这恰好是一个块.所以 timesRepeat: 很高兴,它在 a 为奇数的每次迭代中执行块 b。如果你正确地写成:

10 timesRepeat: [a even ifTrue: b ifFalse: c].

然后在最后,a 将是预期的 -19。

关于您的说法:如果我对块的理解是正确的,那么块的最后一个元素是 returned 而不是求值,这不是真正的案件。块中的最后一条语句没有特殊处理,除了它的结果确实 returned 作为块执行时的块值。块中的最后一条语句只是变量的名称。变量的值恰好是一个块,但不管它是什么,只要有一个变量名本身作为 Smalltalk 中的语句就可以 returns 变量的值。如果变量恰好是一个块,你就得到这个块。该块未执行。

考虑以下块:

[a := 1. b := 2. c := a+b]

执行此块时,a 的值为 1,b 的值为 2,c 的值为 3。该块的值将 return是c的值,也就是3.

[a := 1. b := 2. a]

如果执行此块,结果将是 a 的值,即 1。

[a := 1. b := 2. c := [a+b]. c]

如果你执行这个块,结果将是变量c代表的块。它不执行块 c。这与前面的示例一致。

所以,当你执行块[Transcript show: 'I am called!'. jkl].时,最后的jkl不会被执行。它的价值只是 returned。如果你想执行它,你可以写 asdf := [Transcript show: 'I am called!'. jkl value]. 发送 value 消息时将执行一个块。执行块 [Transcript show: 'I am called!'. jkl value]. 的结果将是执行块 jkl.

的结果

我可能是唯一一个,但我觉得下面第二句有点晦涩:

It just so happens, though, that c is defined as the block [a := a - 2. b] which returns the value of b and that happens to be a block. So timesRepeat: is happy, and it executes the block b on each iteration in which a is odd.

Smalltalk 的语义是:

  1. 评价接受者
  2. 评估参数
  3. 发送消息

顺序很重要,因为计算可能会产生副作用。

所以,实际发生的是:

  1. 接收者 10 的计算结果为 10
  2. 参数 a even ifTrue: b ifFalse: c 的计算结果为 b。副作用 a = -1.
  3. 消息10 timesRepeat: b10 timesRepeat: [a := a + 1]已发送

因此,a = 9.