2011-07-07 38 views
5

,我不是做以下行爲的意義,得到(見in this SO thread):在一個匿名塊

def def_test 
    puts 'def_test.in' 
    yield if block_given? 
    puts 'def_test.out' 
end 

def_test do 
    puts 'def_test ok' 
end 

block_test = proc do |&block| 
    puts 'block_test.in' 
    block.call if block 
    puts 'block_test.out' 
end 

block_test.call do 
    puts 'block_test' 
end 

proc_test = proc do 
    puts 'proc_test.in' 
    yield if block_given? 
    puts 'proc_test.out' 
end 

proc_test.call do 
    puts 'proc_test ok' 
end 

輸出:

def_test.in 
def_test ok 
def_test.out 
block_test.in 
block_test ok 
block_test.out 
proc_test.in 
proc_test.out 

我不介意訴諸明確聲明&塊變量,並直接調用它,但我更想理解爲什麼我最終需要。

+0

相關:http://stackoverflow.com/questions/6628936/extending-a-class-method-in-a-module –

回答

4

lambda的是一個封閉件,它似乎被捕獲從其外範圍block_given?和塊。這種行爲確實有意義,因爲該塊或多或少是外部方法的隱含參數;如果需要,您甚至可以在指定參數中捕獲塊:

def def_test(&block) 
    frobnicate &block 
end 

因此,塊即使在未命名時也是參數列表的一部分。

考慮這種簡單的代碼:

def f 
    lambda do 
     puts "\tbefore block" 
     yield if block_given? 
     puts "\tafter block" 
    end 
end 

puts 'Calling f w/o block' 
x = f; x.call 
puts 

puts 'Calling f w/ block' 
x = f { puts "\t\tf-block" }; x.call 
puts 

puts 'Calling f w/o block but x with block' 
x = f; x.call { puts "\t\tx-block" } 
puts 

puts 'Calling f w/ block and x with block' 
x = f { puts "\t\tf-block" }; x.call { puts "\t\tx-block" } 

這產生對我以下與1.9.2:

Calling f w/o block 
    before block 
    after block 

Calling f w/ block 
    before block 
     f-block 
    after block 

Calling f w/o block but x with block 
    before block 
    after block 

Calling f w/ block and x with block 
    before block 
     f-block 
    after block 

此外,Proc#call(AKA proc ===)不取一塊:

prc === obj→result_of_proc
調用塊,用obj作爲塊的參數。它允許proc對象成爲case語句中when子句的目標。

比較與所述文檔的第一行對於Enumerable#chunk(例如):

enum.chunk {| ELT | ...}→an_enumerator

{...}表明chunk是記錄採取塊,爲Proc#call缺乏這種符號的表示Proc#call並不需要一個塊。

這不完全是一個權威的答案,但它可能會清除一點點。

+0

我想說它比你想象的更具權威性:兩個小時的谷歌搜索沒有取得任何進展。謝謝你代表下一個碰到這個主題的新紅寶石主義者! –

+0

@Denis:如果我可以指出一些官方文檔說「lambdas捕獲塊與其他所有內容」,我會稱之爲權威的,我猜'proc#調用「文檔結合」隱藏'&塊參數「參數與此非常接近。用Ruby搜索常見的Ruby事物確實會產生(哈哈)挫敗感,但隨着ruby-doc.org和apidock.com被推高排名,它越來越好。 –

5

block_given?認爲def範圍,不lambda範圍:

def test 
    l = lambda do 
    yield if block_given? 
    end 
    l.call 
end 

test { puts "In block" } 
+0

對不起,但這不是我希望的答案。我的問題是爲什麼'l.call {puts「在塊」}'中工作,而不是'test {puts「在塊」}'中。如果你編輯你的函數來調用'l.call&Proc。新的'我最初的印象是它會工作(因爲它將與一個正確定義的功能);但它不,我想了解爲什麼。 –

+0

@Denis這取決於你的意思是「工作」。 'yield'和'block_given?'總是在'def'範圍內工作,'lambda'範圍對於兩者都是透明的。將塊傳遞給'lambda'不會改變它。你可以使用'lambda do |&f |'參數傳遞給'lambda'的塊,而不是'yield'或'block_given?'。 'lambda'與廣告「def」不同。 –

+0

是的,它最終「勾選」,但它只是在閱讀mu的答案後才這麼做。我回想起來,你的答案在哪些方面確實有效。只是對於像我這樣的紅寶石新手來說,穆的更詳細的回答使得它更清楚發生了什麼。 –