理解smalltalk中奇怪的逻辑运算符
Understanding weird logical operators in smalltalk
所以我的问题如下:
当 char = 0
boolean = char ~= 0 & char ~= 256
评估为真,如果我像这样反转语句:
boolean = char ~= 256 & char ~= 0
我猜错了。
发生了什么事?我期望这两种情况都是错误的。
正如@Uko 所说,您必须了解消息的优先级:所有二进制消息(+ = < & ~= 等)都是从左到右计算的。
因此你评价:
(((boolean = char) ~= 256) & char) ~= 0
我想你是在追求:
boolean := (char ~= 256) & (char ~= 0).
那么你的表情会怎样?
boolean
大概是单元化的(因此为零)
char
为 0。
boolean = char
为假。
false ~= 256
是真的。
true & char
是字符(原因见下文)
char ~= 0
为假(因为 char = 0)
如果将 0 和 256 取反,则只有最后一步发生变化并且答案为真。
有趣的部分是 class 中 message & 的实现:它可能没有断言参数是布尔值,看起来像:
& aBoolean
^aBoolean
如果您传递的不是布尔值(例如您的情况下的 0),它将 return 这个东西,无论它是什么令人惊讶的......
如果您使用 IDE(Squeak/Pharo/Visualworks/Dolphin... 但不是 gnu Smalltalk)我建议您使用菜单 Debug It
并在调试器中逐步计算表达式。
最后,请注意 char 在 Smalltalk 上下文中可能不是一个好名字:它可能会产生误导。实际上,如果它包含 0,则它更像是一个整数,而不是一个字符。
我们在一些答案中重复了一些我认为值得进一步澄清的内容。我们说评估从左到右。没错,但消息的实际语义是:
先求接收者,再求参数;终于发消息了
由于 Smalltalk VM 是基于堆栈的,这条规则意味着:
- 首先评估接收者,然后将结果压入堆栈。
- 参数按顺序求值,结果压入堆栈。
- 消息已发送
项目 3 意味着发送调用的方法将按照上面定义的顺序在堆栈中找到接收者和参数。
例如,在
a := 1.
b := 2.
b := a - (a := b)
变量 b
的计算结果为 (1 - (a := 2)) = -1
,a
的计算结果为 2
。为什么?因为在评估赋值 a := b
时,减法的接收者 a
已经用当时的值推送,即 1
.
另请注意,即使 VM 碰巧使用寄存器而不是堆栈,也必须保留此语义。原因是评估必须保留语义,因此也必须保留顺序。这一事实对本机代码可能实现的优化有影响。
有趣的是,这种语义以及优先级一元 > 二元 > 关键字以简单的方式支持多态性。比方说 *
比 +
更优先的语言假定 *
是乘法,而 +
是加法。然而,在 Smalltalk 中,由程序员决定这些(和任何其他)选择器的含义,而语法不会妨碍实际语义。
我这里的观点是"left to right"来自于我们用英文写Smalltalk,是从"left to right"读来的。如果我们使用人们从 "right to left" 阅读的语言来实现 Smalltalk,那么这条规则将是矛盾的。实际的定义,将保持不变,就是上面第1、2、3项所表达的,来自栈的隐喻。
所以我的问题如下:
当 char = 0
boolean = char ~= 0 & char ~= 256
评估为真,如果我像这样反转语句:
boolean = char ~= 256 & char ~= 0
我猜错了。
发生了什么事?我期望这两种情况都是错误的。
正如@Uko 所说,您必须了解消息的优先级:所有二进制消息(+ = < & ~= 等)都是从左到右计算的。
因此你评价:
(((boolean = char) ~= 256) & char) ~= 0
我想你是在追求:
boolean := (char ~= 256) & (char ~= 0).
那么你的表情会怎样?
boolean
大概是单元化的(因此为零)char
为 0。boolean = char
为假。false ~= 256
是真的。true & char
是字符(原因见下文)char ~= 0
为假(因为 char = 0)
如果将 0 和 256 取反,则只有最后一步发生变化并且答案为真。
有趣的部分是 class 中 message & 的实现:它可能没有断言参数是布尔值,看起来像:
& aBoolean
^aBoolean
如果您传递的不是布尔值(例如您的情况下的 0),它将 return 这个东西,无论它是什么令人惊讶的......
如果您使用 IDE(Squeak/Pharo/Visualworks/Dolphin... 但不是 gnu Smalltalk)我建议您使用菜单 Debug It
并在调试器中逐步计算表达式。
最后,请注意 char 在 Smalltalk 上下文中可能不是一个好名字:它可能会产生误导。实际上,如果它包含 0,则它更像是一个整数,而不是一个字符。
我们在一些答案中重复了一些我认为值得进一步澄清的内容。我们说评估从左到右。没错,但消息的实际语义是:
先求接收者,再求参数;终于发消息了
由于 Smalltalk VM 是基于堆栈的,这条规则意味着:
- 首先评估接收者,然后将结果压入堆栈。
- 参数按顺序求值,结果压入堆栈。
- 消息已发送
项目 3 意味着发送调用的方法将按照上面定义的顺序在堆栈中找到接收者和参数。
例如,在
a := 1.
b := 2.
b := a - (a := b)
变量 b
的计算结果为 (1 - (a := 2)) = -1
,a
的计算结果为 2
。为什么?因为在评估赋值 a := b
时,减法的接收者 a
已经用当时的值推送,即 1
.
另请注意,即使 VM 碰巧使用寄存器而不是堆栈,也必须保留此语义。原因是评估必须保留语义,因此也必须保留顺序。这一事实对本机代码可能实现的优化有影响。
有趣的是,这种语义以及优先级一元 > 二元 > 关键字以简单的方式支持多态性。比方说 *
比 +
更优先的语言假定 *
是乘法,而 +
是加法。然而,在 Smalltalk 中,由程序员决定这些(和任何其他)选择器的含义,而语法不会妨碍实际语义。
我这里的观点是"left to right"来自于我们用英文写Smalltalk,是从"left to right"读来的。如果我们使用人们从 "right to left" 阅读的语言来实现 Smalltalk,那么这条规则将是矛盾的。实际的定义,将保持不变,就是上面第1、2、3项所表达的,来自栈的隐喻。