我可以将块传递给Proc吗?

时间:2013-10-01 23:23:19

标签: ruby

我想知道是否可以将块传递给Proc。简单地将块传递给Proc.call不起作用:

foo = Proc.new {
  yield
}

foo.call {
  puts "test"
}

结果:

  

LocalJumpError:没有给定块(yield)

lambdas也是如此。但是 可以使用方法对象:

class Foo
  def bar
    yield
  end
end

bar = Foo.new.method :bar

bar.call { puts "Success!" }

结果:

  

成功!

奇怪的是,在将方法对象转换为proc之后仍然有效

bar.to_proc.call { puts "Success!" }

结果:

  

成功!

那么为什么从一个块生成的Proc不接受块,但原来是一个方法的Proc呢?是否可以从接受块的块创建Procs?

2 个答案:

答案 0 :(得分:5)

Procs不能接受块作为隐式参数(您正在尝试的格式)。 proc 可以接收其他proc对象作为参数,显式或使用&参数。例如:

a = Proc.new do |&block|
  block.call
end

a.call() {puts "hi"}

yield是一种语言级别的魔法,只能在方法的上下文中使用。

答案 1 :(得分:0)

以上答案并非100%正确,因此无法接受。尤其是零件;

  

Procs不能接受块作为隐式参数(您正在尝试的格式)。 proc可以显式地或使用&参数接收其他proc对象作为参数。

这是错误的。 Procs和Lambda可以在自己的体内调用yield。要记住的事实是Proc/lambda bodies have a lexical scope!这意味着,如果在定义Proc / lambda时存在一个块,yield将成功执行,就像这样;

def foo
  my_proc = Proc.new { yield }
  my_proc.call
end

foo { puts "Hello world!" } # would print "Hello world!"

如您所见,yield发挥了作用!因为在定义Proc时存在障碍。

可以说,Proc在调用时展开为具有阻塞的方法,因此yield起作用。这也是错误的,可以使用以下代码片段轻松地予以反驳;

def foo
  @my_proc ||= Proc.new { yield }
  @my_proc.call
end

foo { puts "Hello again!" } # would print "Hello world!"
foo # would print "Hello world!"

正如您再次看到的,它与在定义Proc时具有有关。

如果您想更好地理解在词法范围内的含义,让我们来看下面的示例。

class Foo
  def self.hello_proc
    Proc.new { puts name }
  end

  def self.name
    "Alice"
  end
end

class Bar
  def self.put_name
    Foo.hello_proc.call
  end

  def self.name
    "Bob"
  end
end

Bar.put_name # would print "Alice"

您可以将以上代码复制并粘贴到irb会话中,以查看输出结果。它之所以加上“ Alice”,是因为在定义Proc时,名称是“ Alice”。