Julia で scanl を使いたい
Julia で scanl に相当するものが見当たらなかったので、勉強がてら、考えてみました。
foldl が参考になるのではないかと思ってソースを見てみたところ、以下のようになっています。
foldl(op, v0, itr) = mapfoldl(identity, op, v0, itr) mapfoldl(f, op, v0, itr) = mapfoldl_impl(f, op, v0, itr, start(itr)) function mapfoldl_impl(f, op, v0, itr, i) if done(itr, i) return r_promote(op, v0) else (x, i) = next(itr, i) v = op(r_promote(op, v0), f(x)) while !done(itr, i) @inbounds (x, i) = next(itr, i) v = op(v, f(x)) end end end
色々呼び出していますが、f は map 用の関数で、op は v に結果を次々と付け加えていく関数と考えれば良さそう。
Haskell と(雰囲気だけですが)対応させると、こんな感じでしょうか。
itr :: (Ix i) => (b, i) f :: b -> a v :: a op :: a -> a -> a -- mappend 相当 mapfoldl f op v0 itr = foldl op v0 $ map f itr
scanl を作るためには、2つの引数を取る関数とmappend が必要ですが、 mapreduce には前者がないので、これで scanl を実現するのはちょっと難しそうです。
以上を踏まえ、素直に書くことにしました。こんな感じです。
function scanl{T}(op, v0 :: T, xs) function scanList(op, vs, xs) isempty(xs) ? vs : scanList(op, [vs; op(vs[end], xs[1])], xs[2:end]) end scanList(op, [v0], xs) end
再帰を使っていますが、foldl の実装を見ると素直にループを回した方が良いのかもしれません。この辺りも、Julia なら簡単に検討できるはずなので、いずれやってみようと思います。