这个问题在Python中呈现了Haskell的scanl
版本,但是这个函数是否有Ruby版本?
您可以使用reduce()
并自己实现它。
def scanl(op, init, range)
op = op.to_proc unless op.is_a?(Proc)
range.reduce([init]) { |a, e| a.push(op.call(a.last,e)) }
end
p scanl(lambda { |a, b| a + b }, 0, 1..10)
#=> [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
p scanl(:+, 0, 1..10)
#=> [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]
或者您可以使用map()
并将初始元素放在数组的前面。
def scanl(op, init, range)
op = op.to_proc unless op.is_a?(Proc)
acc = init
range.map { |e| acc = op.call(acc, e) }.unshift(init)
end
在Enumerator
类存在的Ruby 2.0中,我们可以构建一个更好的实现,它可以正确地处理无限范围:
def scanl(elem, &op)
Enumerator.new do |yielder|
acc = elem
loop do
yielder << acc
acc = op.call(acc)
end
end.lazy
end
并像这样使用:
scanl(1, &:next).take(10)
#=> #<Enumerator::Lazy: ...>
scanl(1, &:next).take(10).force
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
fib = scanl([0,1]) {|x, y| [y, x + y]}.map(&:first)
fib.take(10).force
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
也许可以写一个更好的版本,但这是我想到的:
require 'spec_helper'
class Array
def scanl accumulator, &block
results = [accumulator] + self
results.each_with_index do |e, i|
results[i] = block.call(accumulator, e)
accumulator = results[i]
end
results
end
end
describe "#scanl" do
it 'is similar to foldl, but returns a list of successive reduced values from the left' do
# example from http://learnyouahaskell.com/higher-order-functions
expect([3, 5, 2, 1].scanl(0, &:+)).to eq([0,3,8,10,11])
end
end
我考虑改变scanl
只是一个方法名,如:+
,而不是一个块,更像reduce
。想法吗?