2012-06-20 67 views
2

有人可以向我解釋下面的代碼正在做什麼。 beforeafter是散列值。無法理解以下代碼的作用

def differences(before, after) 
    before.diff(after).keys.sort.inject([]) do |diffs, k| 
     diff = { :attribute => k, :before => before[k], :after => after[k] } 
     diffs << diff; diffs 
    end 
end 

它是從papertrail不同的寶石。

+0

'inject'的使用有些複雜。對於一個很好的博客文章,以'inject'爲基礎開始,並建立起來,像你在這裏的例子,看看這個:http://blog.jayfields.com/2008/03/ruby-inject.html。 – Telemachus

+2

這裏'inject'的使用在語義上可能是錯誤的,Ruby 1.9增加了['each_with_object'](http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-each_with_object)這個例子更有意義。 –

+2

@AndrewMarshall:不需要'inject'和'each_with_object',這應該是一個'map',一個簡單而簡單的'map'。 – tokland

回答

3

這是密集的代碼,沒問題。所以,正如你所說的beforeafter是作爲參數遞交到方法中的散列(類似?)對象。調用before.diff(after)會返回另一個散列,然後立即調用.keys。這將返回diff返回的散列中的所有密鑰。密鑰以數組形式返回,然後立即排序。

然後我們得到最複雜/密集的位。在該排序的鍵數組上使用inject,該方法建立一個數組(在inject塊內部稱爲diffs),該數組將作爲inject方法的返回值。這個數組是由差異記錄組成的。每條記錄都是散列 - 通過從before.diff(after)返回值的排序數組中獲取一個鍵來構建。這些散列存儲正在被擴散的屬性,它在前面的散列中看起來像什麼,以及它在後面的散列中看起來像什麼。

所以,簡而言之,該方法獲得了兩個哈希值之間的一堆差異,並將它們收集在一個哈希數組中。該散列數組是該方法的最終返回值。

注意inject可以,並且往往比這更簡單得多。通常它只是簡單地將一組值減少到一個結果,一次又一次地應用一個操作並將結果存儲在一個累加器中。你可能知道inject爲其他語言的reduce; reduce是Ruby中的inject的別名。這裏有一個簡單得多的例子:inject

[1,2,3,4].inject(0) do |sum, number| 
    sum + number 
end 
# => 10 

0是累加器 - 初始值。在配對|sum, number|中,sum將是累加器,並且number將依次爲數組中的每個數字。 inject所做的是將1加1,將結果存儲在sum中進行下一輪,將2加到sum,再將結果存儲在sum中等等。累加器sum的單個最終值將作爲返回值。這裏10.在你的例子中增加的複雜性是,累加器與塊內的值不同。這是不常見的,但不壞或單一。 (編輯:安德魯·馬歇爾使好點,也許這是不好見他對原來的問題發表意見,並@tokland指出,這裏的inject只是一個非常過分複雜的地圖替代它壞。 。)請參閱我鏈接到您的問題的評論中的文章,以獲取更多inject的示例。

編輯:@tokland在一些評論中指出,代碼似乎只需要一個簡單的map。這會讀起來容易得多。

def differences(before, after) 
    before.diff(after).keys.sort.map do |k| 
    { :attribute => k, :before => before[k], :after => after[k] } 
    end 
end 

我太專注於解釋代碼在做什麼。我什至想不到如何簡化它。

1

它發現之前和之後的條目因底層對象而異,然後以更方便的格式構建這些差異的列表。

before.diff(after)找到不同的條目。

keys.sort給你以排序的順序的鍵(的差異的地圖)

inject([])就像map,但初始化爲空數組的diff開始。

該塊爲這些差異中的每一個創建一個diff行(哈希),然後將其附加到diffs

+4

'inject',又名'reduce',與'map'完全不同。他們一起思考,因爲他們對集合進行操作,就像'select','each','partition'等一樣,但他們實際做的*有很大的不同。 'map'將一些代碼塊應用到數組中的每個項目並返回一個結果數組。例如:[1,2,3,4] .map {| x | x * x}'返回'[1,4,9,16]'。 'inject'將一個操作應用於數組中的每個項目並返回單個結果。例子'[1,2,3,4] .inject(:+)'對這些數字應用加法並返回'10'。 – Telemachus

+0

@Telemachus:凱文是正確的,該片段做了一個'inject([])',接着是一個'acc << something'。這是一個簡單而簡單的'map',原作者不應該使用'inject'。 – tokland

+0

@tokland我認爲代碼應該使用地圖,並且我更新了包含該代碼的答案。不過,凱文的評論仍然存在誤導。起初,我認爲注入就像地圖時期。這就是我開始試圖迴應的。但是我認爲即使在重新閱讀完整個註釋之後,它仍然是一種誤導:「inject([])'就像'map',但是以'diffs'開始,並初始化爲一個空數組。 「but」似乎不合適,因爲map本身是以空數組開始的。 – Telemachus