Haskellの foldr と foldl と foldl'
前の記事で、Haskellでfoldlを使った場合の動作について書きました。
ここで、Haskellでfoldlを実際に使ってみて動作を確認してみようと思います。
{- fold1.hs -} import Data.List result = foldl (&&) True (map even [1..1000000]) main = print result
colinux> ghc --version The Glorious Glasgow Haskell Compilation System, version 7.0.4
colinux> ghc fold1.hs [1 of 1] Compiling Main ( fold1.hs, fold1.o ) Linking fold1 ... colinux> /usr/bin/time ./fold1 Stack space overflow: current size 8388608 bytes. Use `+RTS -Ksize -RTS' to increase it. Command exited with non-zero status 2 0.58user 1.23system 0:01.91elapsed 94%CPU (0avgtext+0avgdata 361712maxresident)k 0inputs+0outputs (0major+22665minor)pagefaults 0swaps
あっという間にスタックオーバーフローしました。
代わりに foldl' を使ってみます。
{- fold1_.hs -} import Data.List result = foldl' (&&) True (map even [1..1000000]) main = print result
同じく実行してみます。
colinux> ghc fold1_.hs [1 of 1] Compiling Main ( fold1_.hs, fold1_.o ) Linking fold1_ ... colinux> /usr/bin/time ./fold1_ False 0.08user 0.00system 0:00.08elapsed 100%CPU (0avgtext+0avgdata 7328maxresident)k 0inputs+0outputs (0major+515minor)pagefaults 0swaps
ほぼ一瞬で実行が終了しました。
foldrでも試してみます。
{- fold2.hs -} import Data.List result = foldr (&&) True (map even [1..1000000]) main = print result
colinux> ghc fold2.hs [1 of 1] Compiling Main ( fold2.hs, fold2.o ) Linking fold2 ... colinux> /usr/bin/time ./fold2 False 0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata 5264maxresident)k 0inputs+0outputs (0major+386minor)pagefaults 0swaps colinux>
実行時間が短くて比較になりませんが、max resident sizeが若干小さくなっています(7328k -> 5264k)。
スタックオーバーフローしたfoldl版はともかく、foldl'とfoldrの実行時間が比較にならなかったので、リストのサイズを100倍にしてみました。
foldl'での実行結果
colinux> ghc fold1__big.hs [1 of 1] Compiling Main ( fold1__big.hs, fold1__big.o ) Linking fold1__big ... colinux> /usr/bin/time ./fold1__big False 3.75user 0.00system 0:03.75elapsed 100%CPU (0avgtext+0avgdata 7312maxresident)k 0inputs+0outputs (0major+514minor)pagefaults 0swaps
foldrでの実行結果
colinux> ghc fold2_big.hs [1 of 1] Compiling Main ( fold2_big.hs, fold2_big.o ) Linking fold2_big ... colinux> /usr/bin/time ./fold2_big False 0.00user 0.00system 0:00.00elapsed ?%CPU (0avgtext+0avgdata 5264maxresident)k 0inputs+0outputs (0major+387minor)pagefaults 0swaps
foldl'では実行時間がかなり延びましたが、foldrは相変わらず計測不能な時間内に終了しました。
最適化を有効にしてみると……
ちなみに、 ghc -O2 で最適化を有効にすると、foldl と foldl' の差がほとんどなくなります。
foldl(入力リストのサイズは100,000,000)
colinux> ghc -O2 fold1_big.hs [1 of 1] Compiling Main ( fold1_big.hs, fold1_big.o ) Linking fold1_big ... colinux> /usr/bin/time ./fold1_big False 1.71user 0.00system 0:01.72elapsed 99%CPU (0avgtext+0avgdata 7184maxresident)k 0inputs+0outputs (0major+506minor)pagefaults 0swaps
foldl'(同条件)
colinux> ghc -O2 fold1__big.hs [1 of 1] Compiling Main ( fold1__big.hs, fold1__big.o ) Linking fold1__big ... colinux> /usr/bin/time ./fold1__big False 1.81user 0.01system 0:01.82elapsed 100%CPU (0avgtext+0avgdata 7184maxresident)k 0inputs+0outputs (0major+506minor)pagefaults 0swaps
それでも、最適化なしのfoldrには追いつけませんでした。