少し大きな視点で他の回答を補完するには、遅延リストでfoldl'
は、リストを返す関数で使用することは通常悪い考えです。 foldl'
これは、リストを厳密な (非遅延) スカラー値に縮小する場合 (たとえば、リストの合計) に役立つことがよくあります。しかし、結果としてリストを作成している場合foldr
は、怠惰のため、通常はより優れています。コンストラクターは遅延して:
いるため、実際に必要になるまでリストの末尾は計算されません。
あなたの場合:
newList_foldr lst = foldr (\x acc -> x*2 : acc) [] lst
これは実際には次と同じmap (*2)
です:
newList_foldr lst = map (*2) lst
map f lst = foldr (\x acc -> f x : acc) [] lst
評価 (最初のmap
-less 定義を使用):
newList_foldr [1..10]
= foldr (\x acc -> x*2 : acc) [] [1..10]
= foldr (\x acc -> x*2 : acc) [] (1:[2..10])
= 1*2 : foldr (\x rest -> f x : acc) [] [2..10]
これは、Haskell がいつnewList [1..10]
強制されたかを評価するのとほぼ同じです。この結果の消費者がそれを要求した場合にのみ、さらに評価を行い、消費者を満足させるのに必要なだけ評価します。たとえば、次のようになります。
firstElem [] = Nothing
firstElem (x:_) = Just x
firstElem (newList_foldr [1..10])
-- firstElem only needs to evaluate newList [1..10] enough to determine
-- which of its subcases applies—empty list or pair.
= firstElem (foldr (\x acc -> x*2 : acc) [] [1..10])
= firstElem (foldr (\x acc -> x*2 : acc) [] (1:[2..10]))
= firstElem (1*2 : foldr (\x rest -> f x : acc) [] [2..10])
-- firstElem doesn't need the tail, so it's never computed!
= Just (1*2)
これは、foldr
-basednewList
が無限リストでも機能することも意味します。
newList_foldr [1..] = [2,4..]
firstElem (newList_foldr [1..]) = 2
一方、を使用する場合はfoldl'
、常にリスト全体を計算する必要があります。これは、無限リストを操作できないことも意味します。
firstElem (newList_good [1..]) -- doesn't terminate
firstElem (newList_good [1..10])
= firstElem (foldl' (\acc x -> x*2 : acc) [] [1..10])
= firstElem (foldl' (\acc x -> x*2 : acc) [] (1:[2..10]))
= firstElem (foldl' (\acc x -> x*2 : acc) [2] [2..10])
-- we can't short circuit here because the [2] is "inside" the foldl', so
-- firstElem can't see it
= firstElem (foldl' (\acc x -> x*2 : acc) [2] (2:[3..10]))
= firstElem (foldl' (\acc x -> x*2 : acc) [4,2] [3..10])
...
= firstElem (foldl' (\acc x -> x*2 : acc) [18,16,14,12,10,8,6,4,2] (10:[]))
= firstElem (foldl' (\acc x -> x*2 : acc) [20,18,16,14,12,10,8,6,4,2] [])
= firstElem [20,18,16,14,12,10,8,6,4,2]
= firstElem (20:[18,16,14,12,10,8,6,4,2])
= Just 20
foldr
に基づくアルゴリズムは を計算するのに 4 ステップかかりましたが、firstElem_foldr (newList [1..10])
に基づくアルゴリズムfoldl'
は 21 ステップのオーダーでした。さらに悪いことに、4 つのステップは一定のコストですが、21 は入力リストの長さに比例します。<code>firstElem (newList_good [1..150000]) は 300,001 ステップかかりますfirstElem (newList_foldr [1..150000]
がfirstElem (newList_foldr [1..]
、その問題。
また、一定のスペースと一定の時間で実行されることに注意してくださいfirstElem (newList_foldr [1.10])
(一定のスペース以上を割り当てるには、一定以上の時間が必要です)。foldl
厳密な言語からのプロトリズム—「foldl
末尾再帰であり、定数空間で実行され、foldr
末尾再帰ではなく、線形空間で実行されるか、さらに悪い」—Haskellでは当てはまりません。