2012-12-02 91 views
2

我有以下代码:Proc.new如何在此代码中找到该块?

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 } 

回答

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. 如果不是,则引发错误

源代码改变为可读性。访问GitHub上链接的原始文件。

+1

很好的回答和解释! – user931392

0

the manual

创建绑定到当前上下文的新Proc对象。 Proc :: new可能在没有块的情况下调用 ,仅在具有附加块的方法中调用 ,在这种情况下该块将转换为Proc对象。

1

如果没有块调用,它将传递给方法(如果有)的块并将其转换为proc对象。

如果当你调用foo不传递任何块,这将引发ArgumentError例外,这是足以合理,造成block_given?返回false,并尝试使用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 

yield将执行给定的块,因此你不需要明确地将其转换为proc,也不需要通过call