2016-10-10 64 views
3

起初看起來很簡單。這是第一個代碼塊,我在幾分鐘內寫道:如何創建注入方法的自定義實現?

module Enumerable 

    def my_inject(memo=false) 
    memo = memo ? memo : self[0] 
    self.my_each do |item| 
     memo = yield(memo, item) 
    end 
    return memo 
    end 

end 

當注射方法被調用的初始值,它按預期工作。但我一直無法找到一種方法,使其正常工作沒有任何初始值。

例如:

puts [1,2,3,4].my_inject(0) { |memo, i| memo+i} #=> 10 
puts [1,2,3,4].my_inject { |memo, i| memo+i} #=> 11 

而這就是原來的Ruby注入方法輸出:

puts [1,2,3,4].inject { |memo, i| memo+i} #=> 10 

Ruby的文檔說

如果你沒有明確指定的初始值對於備忘錄,則集合的第一個元素被用作備忘錄的初始值。

那麼如何爲初始值設置正確的數據類型呢?我們是否應該在Ruby中爲每種數據類型設置條件,併爲每種數據類型設置默認值?

回答

2

如果調試Ruby的自己的實現,你會看到,它的情況下,沒有默認備忘錄給出的第二個元素開始迭代:

> [1,2,3].inject { |m, i| puts "M: #{m} | I: #{i}"; break } 
M: 1 | I: 2 

因此您的實現應該是這樣的:

def my_inject(*args) 
    init = args.size > 0 
    memo = init ? args[0] : self[0] 

    self.drop(init ? 0 : 1).my_each do |item| 
    memo = yield(memo, item) 
    end 

    return memo 
end 

puts [1,2,3,4].my_inject(0) { |memo, i| memo+i} #=> 10 
puts [1,2,3,4].my_inject { |memo, i| memo+i} #=> 10 

init = args.size > 0的原因是你必須考慮到有人可能想要做[...].my_inject(false) { ... }。因此,您不能檢查memo == false以確定是否需要跳過第一個元素,您必須檢查給出的實際參數計數。

+0

感謝您的回答和解釋「假」的情況。你是對的。 – scaryguy

0

那麼如何爲初始值設置正確的數據類型呢?我們是否應該在Ruby中爲每種數據類型設置條件,併爲每種數據類型設置默認值?

不,默認值是集合的第一個元素。

Ruby允許您定義自己的類,因此無論如何不可能爲每種可能的類型編寫一個案例,其中包含無數的類。

0

只是出於好奇尾遞歸解決方案:

class Array 
    def my_inject(memo = nil, enum = self.each) 
    return enum_for(:my_inject) unless block_given? 

    memo = enum.next if memo.nil? 
    my_inject(yield(memo, enum.next), enum, &Proc.new) 
    rescue StopIteration 
    memo 
    end 
end 

[1,2,3].my_inject(3) { |memo, v| memo += v } 
#⇒ 9 
[1,2,3].my_inject &:* 
#⇒ 6