9

背景

抓住每个 Smalltalk 新手的东西是add:不返回“自我”,而是返回被添加的对象。

例如,使用以下代码:

myCollection := OrderedCollection new 
  add: 'Peter';
  add: 'John';
  add: 'Paul'.

myCollection将包含字符串“Paul”,而不是集合本身。

这是因为add:返回要添加的对象,整个级联表达式的计算结果是最后发送的消息。

相反,它应该写yourself在最后:

myCollection := OrderedCollection new 
  add: 'Peter';
  add: 'John';
  add: 'Paul';
  yourself.

问题

  • 为什么会这样?
  • 这是怎么设计的?
  • add:以这种方式行事有什么好处?
4

5 回答 5

12

我想了很多。我从未听说过 Smalltalk 的任何原始设计者为这个决定辩护,所以我们不确定他们为什么这样做。我已经确定原因是由于级联。如果 add: 返回接收者,那么 (things add: thing1) add: thing2 将与 things add: thing1; 相同。添加:东西2。通过 add: 返回参数,这两个表达式是不同的,程序员可以在适当的时候使用它们。

然而,我认为这是一个错误。我教 Smalltalk 已经超过 25 年了,每次我教它时,人们都会遇到麻烦。我总是警告他们,但他们仍然会在 add: 时出错。所以,我认为这是一个糟糕的设计决定。

这个设计决定是关于库的,而不是编译器。您可以通过进入集合类并更改它们来更改它。当然,无法预测有多少 Smalltalk 程序会崩溃。集合是如此基础,以至于这种改变与对语言的真正改变一样难。

于 2012-12-27T07:52:45.143 回答
6

在其他语言中,您可以编写:

b[j] = a[i] = e;

如果 at:put:返回 put 对象,这会以某种方式保留在 Smalltalk 中:

collectionB at: j put: (collectionA at: i put: e).

允许这种链接的add:/也存在同样的兴趣:remove:

collectionB add: (collectionA add: anElement).
collectionB add: (collectionA remove: anElement).
于 2012-12-27T01:22:09.063 回答
3

最好将此类方法发送级联起来,并且永远不要依赖它们的返回值。setter 方法也是如此,有时可能返回 self,有时返回参数。假设这些方法的返回值接近随机并且永远不会使用它是最安全的。

于 2012-12-27T08:12:31.600 回答
2

我不能为它辩护,我也不能反驳拉尔夫的经历。

对对称的渴望可能是一个促成因素。鉴于 #remove: 返回已删除的对象,因此 #add: 返回已添加的对象是有意义的。

我认为,简单的例子也会使我们产生偏见。当我们已经有对象要添加到变量中时,或者它是一个简单的文字时,返回的值似乎毫无意义。但是如果我们有(有问题的)代码看起来像这样:

someProfile add: VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd

如果 someProfile 是一个线性列表,我想你可以获取你刚刚添加的值:'ed via last. 但它可能只是一个袋子,或一套。在这种情况下,可以很方便地执行以下操作:

currentSize := someProfile add: VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd

有些人会认为这比:

someProfile add: (currentSize := VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd)

虽然最好的是:

currentSize := VirtualMachine youngSpaceEnd - VirtualMachine oldSpaceEnd.
someProfile add: currentSize
于 2012-12-27T20:27:00.637 回答
0

我想出的最好的解释是让它等同于一个作业。假设你有这样的代码:

(stream := WriteStream on: String new) nextPutAll: 'hello'.
stream nextPut: $!

赋值给一个变量会计算出所赋值的对象。我应该能够用集合替换变量并获得等效的行为:

(array at: 1 put: (WriteStream on: String new)) nextPutAll: 'hello'.
(array at: 1) nextPut: $!

现在,话虽如此,我会责备任何编写此代码的开发人员,因为它不可读。我宁愿把它分成两行:

array at: 1 put: (WriteStream on: String new).
array first
   nextPutAll: 'hello';
   nextPut: $!

这是我能给出的最好理由。这样做是为了使对集合的赋值与对变量的赋值一致,但是如果您利用该功能,您的代码将难以阅读。

于 2013-01-05T01:33:50.790 回答