プログラミング Haskell 7章 高階関数 7.3 畳込関数
reverse を foldr で作る例が面白いので写経。
-- 普通の reverse の定義 reverse :: [a] -> [a] reverse [] = [] reverse (x:xs) = reverse xs ++ [x]
reverse [1,2,3]
を展開すると
reverse 1:(2:(3:[])) (reverse (2:(3:[]))) ++ [1] (reverse (3:[])) ++ [2] ++ [1] (reverse []) ++ [3] ++ [2] ++ [1] [] ++ [3] ++ [2] ++ [1] [3,2,1]
のような動作となる。
cons と逆の関数 snoc を定義すると foldr で reverse 出来る。
snoc :: a -> [a] -> [a] snoc x xs = xs ++ [x]
ghci> (:) 4 [1,2,3] -- cons [4,1,2,3] ghci> snoc 4 [1,2,3] [1,2,3,4]
snoc の様子をトレース。
import Debug.Trace snoc x xs = trace ("snoc "++show x++" "++show xs++" = "++ show xs ++" ++ ["++show x++"]")(xs ++ [x]) reverse' [] = trace ("reverse' = []") [] reverse' (x:xs) = trace ("reverse' = snoc "++ show x ++ " (reverse' "++show xs++")") (snoc x (reverse' xs)) ghci> snoc 4 [1,2,3] snoc 4 [1,2,3] = [1,2,3] ++ [4] [1,2,3,4] ghci> reverse' [1,2,3] reverse' = snoc 1 (reverse' [2,3]) reverse' = snoc 2 (reverse' [3]) reverse' = snoc 3 (reverse' []) reverse' = [] snoc 3 [] = [] ++ [3] snoc 2 [3] = [3] ++ [2] snoc 1 [3,2] = [3,2] ++ [1] [3,2,1] ghci> foldr snoc [] [1,2,3] snoc 3 [] = [] ++ [3] snoc 2 [3] = [3] ++ [2] snoc 1 [3,2] = [3,2] ++ [1] [3,2,1]
snoc は初期値 を受け取り、右からリストの要素をひとつづつ 連結していく。
foldl だと初期値のに左から順に追加されていくので reverse される。
ghci> foldl (\xs x->x:xs) [] [1,2,3] [3,2,1] Prelude Debug.Trace> let cons xs x = trace ("cons "++show xs++" "++show x++" = "++ show x ++": "++show xs)(x:xs) Prelude Debug.Trace> foldl cons [] [1,2,3] [3,2,1] cons [] 1 = 1: [] cons [1] 2 = 2: [1] cons [2,1] 3 = 3: [2,1]