#value:消息发送到块时的返回值是该块中最后一句话的值。所以
[ 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
确实是无稽之谈,但在某些级别确实有意义:)
在块内没有什么比保护子句 -
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:
)交互,作为读者的练习。)
在比较 one 和 two 时,您的代码似乎尝试将 nil 像处理 infinity 值一样处理。根据上下文,以下代码可能更具可读性:
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: 和类似的 testing 方法的一个有用功能是它们返回被评估的块的值。例如
(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:
更好
您想要实现一些中断、继续、退出... Smalltalk 中控制流量的常用方法是使用块。 因此,一个有趣的解决方案是使用带有 Block 返回值的辅助方法来中断流程,如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 ] ].
嗯,有趣多于有用,我希望你能找到更简单、更有表现力的编码方式。
正如 Frank Shearar 所指出的,回报是非本地的。
由于回报是非本地的,因此必须依赖
ifTrue: block ifFalse: block
/ifFalse: block ifTrue: block
/ifNil: block ifNotNil: block
/ifNotNil: block ifNil: block
,并且可能依赖 and: block
/or: block
。
oneIsNil := [ :two |
two ifNil: [
nil.
]
ifNotNil: [
true.
].
].
oneIsNotNil := [ :one :two |
two ifNil: [
false.
]
ifNotNil: [
bothNotNil value: one value: two.
].
].
bothNotNil := [ :one :two |
(one > two) ifTrue: [
true.
]
ifFalse: [
(one < two) ifTrue: [
false.
]
ifFalse: [
nil.
].
].
].
[ :one :two |
one ifNil: [
oneIsNil value: two.
]
ifNotNil: [
oneIsNotNil value: one value: two.
].
] value: 5 value: 5.
当事情开始变得丑陋时(例如:缩进级别太高),请考虑将块分解为变量。请注意,外部块中的临时变量和参数不能以这种方式从内部块中引用。这就是我重新传递论点的原因。