2014-06-13 53 views
5

在這個例子中,紅寶石枚舉鏈

[1, 2, 3].each_with_index.map{|i, j| i * j} 
# => [0, 2, 6] 

我的理解是,由於each_with_index枚舉被鏈接到mapmap行爲就像each_with_index通過使索引的塊內,並返回一個新的數組。

爲此,

[1, 2, 3].map.each_with_index{|i, j| i * j} 
# => [0, 2, 6] 

我不知道我怎麼解釋它。

在這個例子中,

[1, 2, 3, 4].map.find {|i| i == 2} 
# => 2 

我期待的輸出爲[2],假定map被鏈接到find,和map將返回一個新的數組。

另外,我看到:

[1, 2, 3, 4].find.each_with_object([]){|i, j| j.push(i)} 
# => [1] 

[1, 2, 3, 4].each_with_object([]).find{|i, j| i == 3} 
# => [3, []] 

你可以讓我知道如何解釋和理解枚舉連鎖紅寶石?

回答

9

您可能會發現它很有用它們分解表達式,並使用IRB或PRY看到什麼紅寶石在做什麼。讓我們先從:

[1,2,3].each_with_index.map { |i,j| i*j } 

enum1 = [1,2,3].each_with_index 
    #=> #<Enumerator: [1, 2, 3]:each_with_index> 

我們可以用Enumerable#to_a(或Enumerable#entries),以enum1轉換成數組,看看它會被傳遞到下一個枚舉(或如果一個塊它有一個):

enum1.to_a 
    #=> [[1, 0], [2, 1], [3, 2]] 

那裏沒有什麼驚喜。但是enum1沒有阻止。相反,我們正在發送它的方法Enumerable#map

enum2 = enum1.map 
    #=> #<Enumerator: #<Enumerator: [1, 2, 3]:each_with_index>:map> 

你可能會認爲這是一種「複合型」枚舉的。此枚舉確實有一個塊,所以將其轉換爲一個陣列將確認其將通過相同的元件到該塊作爲enum1將有:

enum2.to_a 
    #=> [[1, 0], [2, 1], [3, 2]] 

我們看到,陣列[1,0]是第一要素enum2通入該塊。 「消歧」被施加到這個數組指派的塊的變量的值:

i => 1 
j => 0 

即,紅寶石設置:

i,j = [1,0] 

現在,我們可以通過與發送它的方法each調用enum2塊:

enum2.each { |i,j| i*j } 
    #=> [0, 2, 6] 

下考慮:

[1,2,3].map.each_with_index { |i,j| i*j } 

我們:

enum3 = [1,2,3].map 
    #=> #<Enumerator: [1, 2, 3]:map> 
enum3.to_a 
    #=> [1, 2, 3] 
enum4 = enum3.each_with_index 
    #=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:each_with_index> 
enum4.to_a 
    #=> [[1, 0], [2, 1], [3, 2]] 
enum4.each { |i,j| i*j } 
    #=> [0, 2, 6] 

由於enum2enum4傳遞相同的元素融入到塊,我們看到這是在做同樣的事情只有兩個辦法。

這裏的第三相當於鏈:

[1,2,3].map.with_index { |i,j| i*j } 

我們:

enum3 = [1,2,3].map 
    #=> #<Enumerator: [1, 2, 3]:map> 
enum3.to_a 
    #=> [1, 2, 3] 
enum5 = enum3.with_index 
    #=> #<Enumerator: #<Enumerator: [1, 2, 3]:map>:with_index> 
enum5.to_a 
    #=> [[1, 0], [2, 1], [3, 2]] 
enum5.each { |i,j| i*j } 
    #=> [0, 2, 6] 

爲了進一步藉此一步,假設我們有:

[1,2,3].select.with_index.with_object({}) { |(i,j),h| ... } 

我們:

enum6 = [1,2,3].select 
    #=> #<Enumerator: [1, 2, 3]:select> 
enum6.to_a 
    #=> [1, 2, 3] 
enum7 = enum6.with_index 
    #=> #<Enumerator: #<Enumerator: [1, 2, 3]:select>:with_index> 
enum7.to_a 
    #=> [[1, 0], [2, 1], [3, 2]] 
enum8 = enum7.with_object({}) 
    #=> #<Enumerator: #<Enumerator: #<Enumerator: [1, 2, 3]: 
    #  select>:with_index>:with_object({})> 
enum8.to_a 
    #=> [[[1, 0], {}], [[2, 1], {}], [[3, 2], {}]] 

enum8進入到塊中的第一個元素是數組:然後

(i,j),h = [[1, 0], {}] 

消歧被施加到值分配給所述塊的變量:

i => 1 
j => 0 
h => {} 

注意enum8顯示一個空散列被傳遞在enum8.to_a的三個元素中的每一箇中,但當然這只是因爲Ruby在第一個元素傳入後不知道哈希將看起來像什麼樣。

+1

很好提出 – Ratatouille

1

您提到的方法定義在Enumerable對象上。這些方法的行爲有所不同,具體取決於您是否傳遞一個塊。

  • 當你不通過的塊,它們通常返回一個Enumerator對象,可向其中鏈進一步的方法,如each_with_indexwith_indexmap
  • 當傳遞一個塊到這些方法,它們返回不同種類的對象,這取決於該特定方法的意義。
    • 對於像find這樣的方法,其目的是查找滿足條件的第一個對象,並且將它包裝到數組中並沒有什麼特別的意義,所以它將該對象返回爲裸露。
    • 對於像selectreject這樣的方法,其目的是返回所有相關對象,因此它們不能返回單個對象,並且它們必須包裝在數組中(即使相關對象恰好是單個對象)。
+0

我明白了這一點,但你如何解釋像我上面說的鏈[1,2,3] .each_with_index.map {| i,j | i * j}'和'[1,2,3] .map.each_with_index {| i,j |我* j}'返回完全相同的結果,我想相信那麼 – Ratatouille