块转发¶
要转发捕获的块,您需要使用块参数,并在表达式前加上 &
def capture(&block)
block
end
def invoke(&block)
block.call
end
proc = capture { puts "Hello" }
invoke(&proc) # prints "Hello"
在上面的例子中,invoke
接收一个块。我们不能直接将 proc
传递给它,因为 invoke
不会接收普通参数,只会接收块参数。我们使用 &
指定我们确实想要将 proc
作为块参数传递。否则
invoke(proc) # Error: wrong number of arguments for 'invoke' (1 for 0)
您实际上可以将一个过程传递给一个使用 yield 的方法
def capture(&block)
block
end
def twice(&)
yield
yield
end
proc = capture { puts "Hello" }
twice &proc
上面的代码只是简单地重写为
proc = capture { puts "Hello" }
twice do
proc.call
end
或者,结合 &
和 ->
语法
twice &->{ puts "Hello" }
或者
def say_hello
puts "Hello"
end
twice &->say_hello
转发未捕获的块¶
要转发未捕获的块,您必须使用 yield
def foo(&)
yield 1
end
def wrap_foo(&)
puts "Before foo"
foo do |x|
yield x
end
puts "After foo"
end
wrap_foo do |i|
puts i
end
# Output:
# Before foo
# 1
# After foo
您也可以使用 &block
语法转发块,但这需要您至少指定输入类型,并且生成的代码会涉及闭包,速度会更慢
def foo(&)
yield 1
end
def wrap_foo(&block : Int32 -> _)
puts "Before foo"
foo(&block)
puts "After foo"
end
wrap_foo do |i|
puts i
end
# Output:
# Before foo
# 1
# After foo
如果可以使用 yield
,请尽量避免像这样转发块。还有一个问题是,break
和 next
不允许在捕获的块内使用,因此在使用 &block
转发时,以下代码将无法工作
foo_forward do |i|
break # error
end
简而言之,当涉及到 yield
时,请避免使用 &block
转发。