2016-12-31 48 views
0

我有一個很難理解爲什麼這段代碼工作遞歸扁平化的數組:在Ruby中

def flatten(array, result = []) 
    array.each do |element| 
    if element.is_a? Array 
     flatten(element, result) 
    else 
     result << element 
    end 
    end 
    result 
end 

特別是,爲什麼它工作,而無需指定扁平化方法調用的結果結果數組,像這樣:

def flatten1(array, result = []) 
    array.each do |element| 
    if element.is_a? Array 
     result = flatten(element, result) 
    else 
     result << element 
    end 
    end 
    result 
end 

兩者產生相同的輸出:

p flatten [1,2,[3,4,[5,[6]]]] # [1, 2, 3, 4, 5, 6] 
p flatten1 [1,2,[3,4,[5,[6]]]] # [1, 2, 3, 4, 5, 6] 
+0

[這裏有一個較長的討論(HTTP:// stackoverflow.com/questions/1872110/is-ruby-pass-by-reference-or-by-value#10974116),網站上可能還有更多,但簡短的答案是將'Array result'傳遞給函數正在傳遞實際的對象,而不是它的副本,並且該對象由函數內的'result << element'修改所以當遞歸傳遞時,它不斷地被修改而不需要重新分配 –

回答

0

flatten方法在第6行中破壞性地修改其第二個參數result,並將修改後的數組作爲參數傳遞給第4行中的遞歸調用。不需要從該方法返回任何內容,因爲無論通過哪個數組作爲第二個元素都將被破壞性地修改有附加輸入數組的扁平版本:

my_array = [:foo] 

flatten([1, [2, [3, [4]]]], my_array) 

my_array 
#=> [:foo, 1, 2, 3, 4] 

它通常被認爲是不好的形式來修改作爲參數傳遞的對象或修改,而不是,你知道的輸入參數,只是返回其返回值。它看起來像代碼是由一個C程序員編寫的,他希望使用第二個參數作爲輸出緩衝區。

更地道的紅寶石版本將是這個樣子:

def flatten(ary) 
    ary.each_with_object([]) do |el, res| 
    if el.is_a?(Array) 
     res.concat(flatten(el)) 
    else 
     res << el 
    end 
    end 
end 

或沒有突變的純功能的版本都:

def flatten(ary) 
    ary.inject([]) do |res, el| 
    res + if el.is_a?(Array) 
     flatten(el) 
    else 
     [el] 
    end 
    end 
end