哈斯克尔的扫描的Ruby等价物是什么?



这个问题在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。想法吗?

最新更新