smalltalk块 - 我可以显式设置返回值并停止执行块吗?

时间:2011-09-25 18:45:16

标签: block smalltalk

#value:message的返回值,当发送到块时,是该块中最后一个句子的值。所以[ 1 + 2. 3 + 4. ] value评估为7。 我发现有时难以使用。有没有办法显式设置返回值并停止执行块?

对于练习,尝试重写此块而不使用我想象的#return:消息,看看它有多难看。我一定错过了什么。

[ :one :two |
  one isNil ifTrue: [ two isNil ifTrue: [ self return: nil ] ifFalse: [ self return: true ] ].
  two ifNil: [ self return: false ].

 (one > two)
  ifTrue: [ self return: true ]
  ifFalse: [ (one < two)
              ifTrue: [ self return: false ]
              ifFalse: [ self return: nil ]
            ].
]

编辑:self return: sth确实是无稽之谈,但在某些级别确实有意义:)

3 个答案:

答案 0 :(得分:7)

没有像保护子句一样 - blah ifTrue: [^ foo] - 在一个块内,因为^是非本地返回,从调用块而不是块本身的方法返回。

大块 - 就像大块头一样 - 应该重构为更小,更容易理解/易处理的子部分,但有时候这并不总是可行的。我的意思是这个答案建议当你不能以通常的方式真正简化时尝试的选项。

如果您的块实际上非常复杂,并且您无法将其简化(例如,将其拆分为太多本地信息),那么您可以使用显式返回值。特别是,如果您的块没有返回nil,您可以执行类似

的操作
[:one :two | | result |
    result := (one isNil and: [two isNil]) ifTrue: [false].
    result ifNil: ["do one thing, possibly setting result"].
    result]

如果您的区块可以返回nil,则您需要另一个标记值:

[:one :two | | result marker |
    result := marker := Object new.
    (result == marker) ifTrue: ["do one thing, possibly setting result"].
    result]

最后 - 我毫不犹豫地建议 - 你可以这样做:

[1 + 2.
thisContext return: 5.
3 + 4] value

返回5

(验证这与^#ifTrue:ifFalse:等内联选择器的交互方式是否为读者留下的练习。)

答案 1 :(得分:2)

在比较一个两个时,您的代码似乎会尝试像处理无穷大一样处理 nil 。以下代码可能更具可读性,具体取决于上下文:

a := [:one :two |
    | x y |
    x := one ifNil: [Float infinity].
    y := two ifNil: [Float infinity].
    (x = y) ifTrue: [nil] ifFalse: [x > y]]

#ifTrue的一个有用功能:ifFalse:,#ifNil:ifNotNil:和类似的测试方法是它们返回被评估的块的值。例如(4 > 1) ifTrue: ['greater'] ifFalse: ['not-greater']评估为'更高'。此功能通常可以从尾部位置的嵌套块返回值。

当一个块内的代码变得太复杂时,我建议你将它重构为一个方法。但请参阅弗兰克对解决方法的回答。

修改

正如评论中所指出的,上面的代码假设数字。我还提出了一些适用于其他类似对象的东西:

a:=
[ :one :two |
  true caseOf: {
    [one = two]->[nil].
    [one isNil]->[true].
    [two isNil]->[false]
  } otherwise: [one>two]]

很少使用#caseOf:构造,但它肯定比thisContext return:

更好

答案 2 :(得分:1)

你想实施一些休息,继续,退出...... 在Smalltalk中控制流量的常用方法是使用块。 因此,一个有趣的解决方案是使用带有块返回值的辅助方法来打破流程,如所描述的here

Object>>exitThru: aBlock
    ^aBlock value: [:result | ^result]

现在,让我们看看如何使用它:

| aBlock |
aBlock :=   [ :one :two |
    self exitThru: [:exit |
        one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
        two isNil ifTrue: [ exit value: false ].
        one > two ifTrue: [ exit value: true ].
        one < two ifTrue: [ exit value: false ].
        exit value: nil] ].

#(('abc' nil) (nil nil) (nil 'def') ('y' 'abc') ('y' 'y') ('y' 'z'))
    collect:
        [:pair |
        aBlock value: pair first value: pair last ]
-> #(false nil true true nil false)

编辑我的第一个版本是不必要的复杂,不记得是什么导致我额外的间接:

| aBlock |
aBlock :=  [:wrapOne :wrapTwo |
    self exitThru: [:exit |
        [ :one :two |
        one isNil ifTrue: [ two isNil ifTrue: [exit value: nil ] ifFalse: [ exit value: true ] ].
        two isNil ifTrue: [ exit value: false ].
        one > two ifTrue: [ exit value: true ].
        one < two ifTrue: [ exit value: false ].
        exit value: nil ]
            value: wrapOne value: wrapTwo ] ].

嗯,比有用更有趣,我希望你能找到更简单,更有表现力的代码方式。