haskell - foldl with (++) is a lot slower than foldr -
why foldl slower foldr ? have list of lists "a" like
a = [[1],[2],[3]]
and want change list using fold
foldr (++) []
it works fine (time complexity o(n)). if use foldl instead, slow (time complexity o(n^2)).
foldl (++) []
if foldl folding input list left side,
(([] ++ [1]) ++ [2]) ++ [3]
and foldr right side,
[1] ++ ([2] ++ ([3] ++ []))
the number of computations (++) supposed same in both cases. why foldr slow? per time complexity, foldl looks if scanning input list twice many times foldr. used following computer time
length $ fold (++) [] $ map (:[]) [1..n] (fold either foldl or foldr)
it's because of how ++
works. note is defined induction on first argument.
[] ++ ys = ys (x:xs) ++ ys = x : (xs ++ ys)
the number of recursive calls depends on length xs
.
because of this, in xs ++ ys
more convenient have small xs
, large ys
other way around.
note folding on right achieves this:
[1] ++ ([2] ++ ([3] ++ ...
we have short (o(1)-length) lists on left of ++
, making fold cost o(n).
instead folding on left bad:
((([] ++ [1]) ++ [2]) ++ [3]) ++ ...
the left arguments become larger , larger. hence, o(n^2) complexity here.
this argument can made more precise considering how output list demanded. 1 can observe foldr
produces result in "streaming" fashion, demanding e.g. first output list cell forces little part of input -- first ++
needs performed, , first recursive step needed! instead demanding first item foldl
result force all ++
calls, making quite expensive (even if each call needs 1 recursive step, there o(n) calls).
Comments
Post a Comment