我想做一些看起来像这样的事情:
def make_wrapped_block(&block)
puts "take_block:before"
func = lambda do
puts "Before calling make_wrapped_block's passed block"
block.call
puts "After calling make_wrapped_block's passed block"
end
puts "take block:after"
func
end
def make_block
make_wrapped_block do
puts "Before return"
return :pi
puts "After return"
end
end
make_block.call
..那里会有很多make_block
个方法,它们会生成make_wrapped_block
提供类似初始化和清理的闭包。
因为传递给make_wrapped_block
中的make_block
的块返回,这会导致LocalJumpError:
[ cpm juno ~/tmp/local-jump ] ruby bad.rb
take_block:before
take block:after
Before calling make_wrapped_block's passed block
Before return
bad.rb:15:in `make_block': unexpected return (LocalJumpError)
from bad.rb:5:in `call'
from bad.rb:5:in `make_wrapped_block'
from bad.rb:20:in `call'
from bad.rb:20
现在,我可以使用稍微不同的语法来理解这个想法:
def make_wrapped_block(block)
puts "take_block:before"
func = lambda do
puts "Before calling make_wrapped_block's passed block"
block.call
puts "After calling make_wrapped_block's passed block"
end
puts "take block:after"
func
end
def make_block
make_wrapped_block(lambda {
puts "Before return"
return :pi
puts "After return"
})
end
make_block.call
这是有效的,因为当您从使用lambda
创建的匿名函数返回时,它将退出匿名函数,而使用Proc.new
和匿名块时,它会尝试从其定义的范围返回。不能传递它们并安全返回。
是否有一种安全的方法可以从创建范围之外的传递块返回?
第二种方法效果很好,但语法比第一种方式有点丑。
答案 0 :(得分:2)
好吧,你可以赶上LocalJumpError
:
def make_wrapped_block(&block)
puts "take_block:before"
func = lambda do
puts "Before calling make_wrapped_block's passed block"
begin
block.call
rescue LocalJumpError
# allow return / break
end
puts "After calling make_wrapped_block's passed block"
end
puts "take block:after"
func
end
#...
make_block.call
但这有点难看。
如果你不介意让回报有点尴尬,你可以改用throw
:
def make_wrapped_block(&block)
puts "take_block:before"
func = lambda do
puts "Before calling make_wrapped_block's passed block"
catch (:return) { block.call }
puts "After calling make_wrapped_block's passed block"
end
puts "take block:after"
func
end
def make_block
make_wrapped_block do
puts "Before throw"
throw :return
puts "After throw"
end
end
但是如果对于那些写你make_block
的人来说这可能有点奇怪
方法,除非是你。
答案 1 :(得分:2)
Ruby方法/块/闭包/ lambdas是非常不一致的并且是一个痛苦的屁股。以下是idiosyncrasies的全面指南。
您的示例中的内容是return
。 do...end
闭包不是真正的闭包(出于性能原因)。因此,return
不会退出do...end
,而是返回周围的方法。但是,在lambda
中,return
退出lambda
,而不是封闭方法,这是预期的行为。