foldl について
「畳み込み」を行う関数の一つである foldl 。
*main> foldl (+) 0 [1,2,3] 6
のようにリストの中身である1、2、3を左から足して、結果が6になる、という挙動を示す。いったいどういう仕組みなのだろうか。
foldl の定義は、
foldl :: (a -> b -> a) -> a -> [b] -> a ・・・ 1) foldl step zero (x:xs) = foldl step (step zero x) xs ・・・ 2) foldl _ zero [] = zero ・・・ 3)
foldlは引数を3つとる関数である。
1. step:リストの各値に適用する関数(この場合、+なので足すわけである)
2. zero:初期値。そして、計算途中の値を蓄積する場所である。ここに途中経過を蓄えながら再帰的に計算を行っていく
3. (x:xs):対象となるリスト
具体的に fold (+) 0 [1,2,3] がどのように計算されるか順を追って調べてみよう。
foldl (+) 0 [1,2,3] ==> 2)式左辺を当てはめる step = + zero = 0 (x:xs) = [1,2,3] 、つまり x = 1、xs = [2,3] として 2)式右辺に適用 foldl (+) ((+) 0 1) [2,3] ※ここで、((+) 0 1 ) というのは(0 + 1) と同じだから以降は後者の書き方にするよ、その方がシンプルなので。 よって、 foldl (+) (0 + 1) [2,3] ==> 上記結果を 2)左辺と照らし合わせる step = + zero = (0 + 1) ← リストの最初項目(つまり 1)に関数(+) を適用した途中結果が格納されている (x:xs) = [2,3] 、つまり x = 2、xs = [3] 2)式右辺を適用 foldl (+) ((0 + 1) + 2) [3] ==> 上記結果を 2)左辺と照らし合わせる step = + zero = ((0 + 1) + 2) ← リストの内容のうち、1と2を適用した結果が格納されている (x:xs) = [3]、つまり x = 3、xs = [] xsは空要素となる 2)式右辺を適用 foldl (+) (((0 + 1) + 2) + 3) [] ==> 上記結果を 2)左辺と照らし合わせる step = + zero = (((0 + 1) + 2) + 3) 3つ目の引数は [] である。 3つ目の引数が[] なので 3)式が適用される。つまり返ってくる値はzero。よって、 (((0 + 1) + 2) + 3) ==> 再帰処理は終点まで到達。(((0 + 1) + 2 ) + 3) の値が計算されて答えは 6
もう一度まとめる
foldl (+) 0 [1,2,3] ==> foldl (+) (0 + 1) [2,3] ==> foldl (+) ((0 + 1) + 2) [3] ==> foldl (+) (((0 + 1) + 2) + 3) [] ==> (((0 + 1) + 2) + 3) ==> 6
というわけである。ふむふむ。
ここに至るまで、自分の場合紙と鉛筆が必須でした。流れをガリガリ書かないとなかなか見えてこない。
それにしても、このような計算の経緯を視覚化するツールとか無いのかな。言い出したものが作れの法則なのだろうけど。