Proc.new如何在此代码中找到块?

时间:2012-12-02 18:08:39

标签: ruby search find block proc

我有以下代码:

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 ++实现。

我还有另一个问题:这样的东西只能用红宝石来实现吗?我的意思是, 我可以编写一个方法,如果没有给出块,则获取传递给调用它的方法的块。类似的东西:

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 }

3 个答案:

答案 0 :(得分:2)

Proc.new将使用方法的块,如果在没有附加一个方法的情况下调用该方法的块。 This is documented behavior

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

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. 如果没有,请提出错误

  2. 为了便于阅读,修改了源代码。访问GitHub上的原始链接源文件。

答案 1 :(得分:1)

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

如果在致电foo时未传递任何阻止,则会引发ArgumentError异常,这是合理的,导致block_given?返回false并尝试使用{{ 1}}没有阻挡。

截至第二个问题,您可以使用Proc.new表示法通过多种方法传递proc。

它会将给定的块转换为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 将执行给定的块,因此您不需要将其显式转换为proc,也不需要通过yield执行它

答案 2 :(得分:0)

来自the manual

  

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