2012-12-02 20 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