私の最初の本能は次のとおりでした:「それは明らかにスキャン(別名prefix-sum)なので、簡単なはずです」:
[1, 5, 8, 11, -6].scan(:+)
明らかに、 RubyにはないEnumerable#scan
ので、私は最近HaskellとScalaを読みすぎています…まだ:
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]
この種の問題が再び発生する場合は、別の言語で、スキャンとプレフィックス合計という用語を覚えておいてください。このような関数は通常、かなり一般的です。なぜRubyにまだそれらがないのかよくわかりません。