Ruby 方法可以访问隐式块参数吗?

Can a Ruby method access the implicit block argument?

可以使用 yield 执行传递给 Ruby 方法的隐式块参数,或者可以使用 block_given? 检查其是否存在。我正在尝试处理这个隐式块以将其传递给另一个方法。

这可能吗?

(这是对隐式块参数的访问,我正在询问。用显式参数替换它不会削减它。)

您可以对其进行 procify,更重要的是 给它一个名称,以便您可以引用它,使用 & ampersand unary prefix sigil in the parameter list of the method,像这样:

#implicit, anonymous, cannot be referenced:
def foo
  yield 23 if block_given?
end

foo {|i| puts i }
# 23

#explicit, named, can be referenced:
def bar(&blk)
  yield 23 if block_given? # still works

  blk.(42) if blk # but now it also has a name and is a `Proc`

  # since we have the block available as an object, we can inspect it
  p blk.arity, blk.parameters, blk.source_location, blk.binding

  b = blk.binding
  p b.local_variables.map {|var| [var, b.local_variable_get(var)] }.to_h
end

quux = "Hello"

bar { |a, b, c = nil, d: nil, &e| puts a }
# 23
# 42
# 2
# [[:opt, :a], [:opt, :b], [:opt, :c], [:key, :d], [:block, :e]]
# ["(irb)", 24]
# #<Binding:0x00007fb091051308>
# { :quux => "Hello" }

这是您的两个选择:

  • 隐含的,匿名的,不是对象
  • 显式,命名,Proc

曾经有一个未记录的技巧,实际上是 Proc::new 如何在 MRI 中实现的意外副作用:Proc::new 没有检查你是否通过了一个块,它只是假设您传递了一个块并将第一个块从内部 VM 堆栈的顶部取出。因此,如果您没有将块传递给 Proc::new,它实际上最终会为传递给该方法的隐式块创建一个 Proc(因为那是恰好是在堆栈的顶部)。

但是,这永远无法移植,永远无法保证,永远无法在所有 Ruby 实现中发挥作用,并且 AFAIK 不再适用于 YARV。

看看this answer。在你的情况下,它会是这样的:

def outer
  wrapper = lambda { |something|
    p 'Do something crazy in this wrapper'
    yield(something)
  }
  other_method(&wrapper)
end

def other_method
  yield(5)
end

outer { |x| puts x + 3 }

你得到:

"Do something crazy in this wrapper"
8
=> nil

您可以通过Proc.new引用块参数。来自文档:

::new may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object.

示例:

def bar
  yield * 2
end

def foo
  bar(&Proc.new)
end

foo(123)
#=> 456

请注意 Proc.new 在不传递块的情况下调用时引发 ArgumentError