我想知道如何将一个块传递给一个方法,该方法会在return
上创建方法yield
。
天真的方法不起作用:
def run(&block)
block.call
end
run { return :foo } # => LocalJumpError
包装在另一个过程中具有相同的效果:
def run(&block)
proc { block.call }.call
end
run { return :bar } # => LocalJumpError
所以我认为return
语句绑定到当前receiver
的{{1}}。但是,使用binding
进行尝试证明我错了:
instance_eval
所以问题是:
class ProcTest
def run(&block)
puts "run: #{[binding.local_variables, binding.receiver]}"
instance_eval(&block)
end
end
pt = ProcTest.new
binding_inspector = proc { puts "proc: #{[binding.local_variables, binding.receiver]}" }
puts "main: #{[binding.local_variables, binding.receiver]}"
# => main: [[:pt, :binding_inspector], main]
binding_inspector.call
# => proc: [[:pt, :binding_inspector], main]
pt.run(&binding_inspector)
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => proc: [[:pt, :binding_inspector], #<ProcTest:0x007f4987b06508>]
pt.run { return :baz }
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => LocalJumpError
语句绑定。这个连接是否可以通过语言的API访问?答案 0 :(得分:2)
我认为
return
语句绑定到当前receiver
的{{1}}。
只有方法才有接收器。 binding
不是一种方法:
return
尝试将其作为方法调用不起作用:
defined? return #=> "expression"
尝试使用
def foo send(:return, 123) end foo #=> undefined method `return'
证明我错了
虽然instance_eval
评估接收器上下文中的块(因此您可以访问接收器实例方法和实例变量):
instance_eval
...它不评估当前绑定中的块(因此您无权访问任何局部变量):
class MyClass
def foo(&block)
@var = 123
instance_eval(&block)
end
end
MyClass.new.foo { instance_variables }
#=> [:@var]
如何做到这一点?
您可以使用class MyClass
def foo(&block)
var = 123
instance_eval(&block)
end
end
MyClass.new.foo { local_variables }
#=> []
,但这需要一个字符串:
eval
或者通过将绑定传递给块(再次使用def foo
var = 123
eval yield
nil
end
foo { "return var * 2" }
#=> 246
):
eval
答案 1 :(得分:1)
return
在定义块时从封闭方法返回(即,创建块的闭包)。在您的示例中,没有要返回的封闭块,因此您的例外。
这很容易证明:
def foo(&block)
puts yield
puts "we won't get here"
end
def bar
foo { return "hi from the block"; puts "we never get here" }
puts "we never get here either"
end
puts bar # => "hi from the block" (only printed once; the puts in `foo` is not executed)
在proc中返回会立即返回proc,而不是在proc下的堆栈上的方法:
def foo(&block)
puts yield
puts "we will get here"
end
def bar
foo &->{ return "hi from the proc"; puts "we never get here" }
puts "we will get here too"
end
puts bar
# hi from the proc # puts from foo
# we will get here # puts from foo
# we will get here too # puts from bar
由于这些行为,无法实现所需的行为,其中给定块中的return
将在调用块的方法中执行return
,除非block是在该范围内定义的,因为这样做会要求其中一个现有行为不起作用。
你可以用throw ... catch来实现这样的东西,这有点像从任意深度压缩堆栈的方式,但你不能用它返回任意值:
def foo(&block)
yield
puts "we won't get here"
end
catch(:escape) do
foo &->{ throw :escape }
end