9

我想知道是否可以将块传递给 Proc。简单地传递一个块Proc.call是行不通的:

foo = Proc.new {
  yield
}

foo.call {
  puts "test"
}

结果是:

LocalJumpError:没有给出块(产量)

lambda 也是如此。但是,这确实适用于方法对象:

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?

4

2 回答 2

10

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

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

a.call() {puts "hi"}

yield是一种语言级别的魔法,仅在方法的上下文中有效。

于 2013-10-01T23:42:19.163 回答
1

上述答案并非 100% 正确,因此不能被接受。尤其是部分;

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

这是错误的。Procs 和 lambdas 可以调用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”。

于 2018-11-28T13:31:10.267 回答