内置的inject
非常多态,所以在try 从头开始实现它之前(不查看源代码)
我们需要探索它在不同情况下的表现.我跳过您已经知道的内容(例如,将第一个元素用作
初始值(如果未明确提供等),除此之外:
[1,2,3].inject(0, :+, :foo) #=> ArgumentError: wrong number of arguments (given 3, expected 0..2)
# Max. arity is strict
[1,2,3].inject(0, :+) { 1 } #=> 6
# If both symbol _and_ block are provided, the former dominates
[1,2,3].inject(:+) { |acc, x| acc } #=> :+
# With only 1 parameter and a block the former will be treated as an init value, not a proc.
[1,2,3].inject("+") #=> 6
[1,2,3].inject("+") { |acc, x| acc } #=> "+"
# Strings work too. This is important, because strings _don't respond to `to_proc`_, so we would need smth. else
[1,2,3].inject #=> LocalJumpError: no block given
# Ok, local jump error means that we try to yield in the cases where the 2nd parameter is not provided
[1,2,3].inject(nil) #=> TypeError: nil is not a symbol nor a string
# But if it is provided, we try to do with it something that quacks pretty much like `send`...
考虑到这些内容,我们可以编写如下内容
module Enum
# Just for convenience, you don't need it if you implement your own `each`
include Enumerable
def my_inject(acc = nil, sym = nil)
# With a single attribute and no block we assume that the init value is in fact nil
# and what is provided should be "called" on elements somehow
if acc && !sym && !block_given?
sym, acc = acc, nil
end
each do |element|
if !acc
# If we are not initialized yet, we just assign an element to our accumulator
# and proceed
acc = element
elsif sym
# If the "symbol" was provided explicitly (or resolved as such in a single parameter case)
# we try to call the appropriate method on the accumulator.
acc = acc.send(sym, element)
else
# Otherwise just try to yield
acc = yield acc, element
end
end
acc
end
end
听我说,我们快到了:)让我们来看看它是如何嘎嘎作响的:
class Ary < Array
include Enum
end
ary = Ary.new.push(*[1,2,3])
ary.my_inject #=> LocalJumpError: no block given
ary.my_inject(0) #=> TypeError: 0 is not a symbol nor a string
ary.my_inject("+") #=> 6
ary.my_inject(:+) #=> 6
ary.my_inject(0, :+) #=> 6
ary.my_inject(1, :+) #=> 7
ary.my_inject(1, :+) { 1 } #=> 7
ary.my_inject(:+) { |acc, x| acc } #=> :+
因此,情况大致相同.可能还有其他一些我的实现不能满足的边缘情况,但我把它们留给您:)