我的第一直觉是:“这显然是一个扫描(又名前缀和),所以这应该很容易”:
[1, 5, 8, 11, -6].scan(:+)
显然,我最近读了太多 Haskell 和 Scala,因为Ruby中没有……然而:Enumerable#scan
module Enumerable
def scan(initial=first, &block)
[initial].tap {|res|
reduce {|acc, el|
block.(acc, el).tap {|el|
res << el
}
}
}
end
end
如果你想Enumerable#scan
表现得像Enumerable#reduce
,即采用可选的初始参数和可选的符号,我们需要稍微增强我们的版本,使用从 Rubinius 窃取的一些参数按摩代码Enumerable#reduce
:
module Enumerable
def scan(initial=nil, sym=nil, &block)
args = if initial then [initial] else [] end
unless block_given?
args, sym, initial = [], initial, first unless sym
block = ->(acc, el) { acc.send(sym, el) }
end
[initial || first].tap {|res|
reduce(*args) {|acc, el|
block.(acc, el).tap {|e|
res << e
}
}
}
end
end
有了这个增强版本,上面的例子现在可以工作了:
p [1, 5, 8, 11, -6].scan(:+)
# => [1, 6, 14, 25, 19]
如果您再次遇到此类问题,请记住另一种语言的术语scan和prefix-sum,此类功能通常很常见。我不太明白为什么 Ruby 还没有它们。