2

我有以下代码:

def call_block
  Proc.new.call
  my_local_proc = Proc.new { Proc.new.call }
  my_local_proc.call
end

call_block { p 'block' }

输出是:

block
block

有人可以向我解释一下 Proc.new 如何找到我传递给 call_block 的块吗?我猜 Proc.new 只是搜索最近的块并且它完全用 C++ 实现。

我还有另一个问题:仅使用 ruby​​ 可以实现这样的事情吗?我的意思是,我可以编写一个方法,如果没有给出块,则获取传递给调用它的方法的块。就像是:

def bar
  if not block_given?
    #use the block that has been given to the caller
  end
  # some code
end

def foo
  bar
end

foo { :block }
4

3 回答 3

2

Proc.new如果在一个附加了一个的方法内没有一个调用,将使用该方法的块。这是记录在案的行为

要了解 YARV 是如何做到的,让我们阅读源代码。具体来说,proc_new函数

block_pointer = rb_vm_control_frame_block_ptr(control_frame_pointer);

此行检索指向与当前控制帧关联的块的指针。

我相信这些控制框架实现了 Ruby 的堆栈。我们目前在Proc.new控制框架内,因此这将检索指向该方法的块的指针。

if (block_pointer != NULL) {
    /* block found */
} else {
    /* block not found... */
}

如果指针不是NULL,则Proc.new显式传递了一个块。但是,如果指针 NULL怎么办?

/* block not found... */
control_frame_pointer = RUBY_VM_PREVIOUS_CONTROL_FRAME(control_frame_pointer);
block_pointer = rb_vm_control_frame_block_ptr(control_frame_pointer);

我们在堆栈上向上移动并尝试获取它的块。换句话说,我们向上移动到调用者的控制框架并尝试获取它的块。

if (block_pointer != NULL) {
    if (is_lambda) {
        rb_warn("tried to create Proc object without a block");
    }
} else {
    rb_raise(rb_eArgError, "tried to create Proc object without a block");
}

现在,如果不是NULL,那么我们就成功了。如果它仍然是 NULL,那么我们无法创建Proc,所以我们提出了ArgumentError

该算法归结为:

  1. 看看是否Proc.new给了一个块
    1. 如果是这样,请使用它
    2. 如果没有,请查看是否给调用者一个块
      1. 如果是这样,请使用它
      2. 如果不是,则引发错误

为提高可读性而更改了源代码。访问 GitHub 上的链接源文件以获取原始文件。

于 2012-12-02T18:18:23.030 回答
1

如果Proc.new在没有块的情况下调用,它会将块传递给方法(如果有)并将其转换为 proc 对象。

如果你在调用的时候没有传递任何块foo,它会引发一个ArgumentError异常,这很合理,原因block_given?返回false,你尝试在Proc.new没有块的情况下使用。

&对于第二个问题,您可以使用符号通过多种方法传递 proc 。

它将给定块转换为一个过程,您可以进一步传递它:

def bar &proc
  proc.call
end

def foo &proc
  bar &proc
end

p foo { :block }
# => :block

此外,bar方法可以这样重写:

def bar
  yield if block_given?
end

yield将执行给定的块,因此您无需将其显式转换为 proc 也无需通过call

于 2012-12-02T18:18:56.837 回答
0

手册

创建一个新的 Proc 对象,绑定到当前上下文。只能在带有附加块的方法中调用 Proc::new 而不使用块,在这种情况下,该块将转换为 Proc 对象。

于 2012-12-02T18:17:58.740 回答