起初看起来很容易。这是我在几分钟内写的第一个代码块:
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
当使用初始值调用inject方法时,它按预期工作。但我无法找到一种方法让它正常工作没有任何初始值。
例如:
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中的每种数据类型设置条件并为每种数据类型设置默认值?
答案 0 :(得分: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
确定是否需要跳过第一个元素,您必须检查给定的实际参数计数。
答案 1 :(得分:0)
那么如何为初始值设置正确的数据类型?我们是否应该为Ruby中的每种数据类型设置条件并为每种数据类型设置默认值?
不,默认值是集合的第一个元素。
Ruby允许你定义自己的类,所以无论如何都不可能为每种可能的类型写一个案例,其中有无数种。
答案 2 :(得分: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