2016-07-07 43 views
1

可能有一個簡單的方法來做到這一點。屈服於一個匿名塊兩個函數向上

我試圖重構像下面

def foo(baz) 
    baz.update_first 
    if baz.has_condition? 
     yield baz.val if block_given? 
     baz.a 
    else 
     baz.b 
    end 
end 

稱爲像

foo(baz) {|b| b.modify} 

喜歡的東西

def foo(baz) 
    baz.update_first 
    bar(baz) {|i| yield i if block_given? }   
end 

def bar(baz) 
    if baz.has_condition? 
     yield baz.val if block_given? 
     baz.a 
    else 
     baz.b 
    end 
end 

請問這項工作?怎麼樣?

我認爲它會,但我會很欣賞一個塊如何產生的清晰解釋工作...閱讀通過proc.c和vm.c和relevant git commit in the ruby source code,我認爲當在foo中調用bar時,它會執行直到它產生,然後你將幀堆棧移動到foo中定義的塊的本地環境指針,該塊被調用,其中yield返回到foo調用的塊,執行它,然後返回到bar 。那是對的嗎?有一個更好的方法嗎?

對我來說,這感覺有點奇怪,就像反轉控制一樣,它需要foo才能更多地瞭解baz,但不幸的是,我不能簡單地在這段代碼中傳遞一個proc或lambda。

回答

3

我想也許yield的概念將更清晰,如果你看看另一種語法,它將集團轉換爲proc參數。

例如,下面的實施例是相同的

def my_each(arr) 
    arr.each { |x| yield x } 
end 

def my_each(arr, &blk) 
    arr.each { |x| blk.call(x) } 
end 

# Both are called the same way 
my_each([1,2,3]) { |x| print x } 
# => 123 

當使用yield,變量是沒有在參數列表中聲明它在該方法中可用。將一個&符號預先加入參數將其轉換爲proc,因此在該方法中可以使用.call運行。

這裏提供一個塊一個方法,然後執行它兩個作用域中的一個例子:

def method_a(number, &blk) 
    method_b do 
    method_c do 
     blk.call(number) 
    end 
    end 
end 

def method_b(&blk) 
    blk.call 
end 

def method_c(&blk) 
    blk.call 
end 

method_a(1) { |num| puts num + 1 } 
# => 2 

注意blk不是一個神奇的詞 - 你能說出任何你想要的變量。

下面是與產量同一件事情:

def method_a(number) 
    method_b do 
    method_c do 
     yield number 
    end 
    end 
end 

def method_b 
    yield 
end 

def method_c 
    yield 
end 

method_a(1) { |num| puts num + 1 } 
# => 2 

我認爲使用&blk語法更清晰,因爲它分配給PROC一個變量。僅僅因爲在該方法中使用了proc並不意味着您必須運行Proc.new。該塊將自動轉換爲proc。