Haskell では、遅延評価のために無限リストを使用できることを思い出してください。したがって、`[1..] は無限に長いにもかかわらず、head [1..]
ちょうど 1 であり、 2 です。head $ map (+1) [1..]
それがわからない場合は、しばらく停止して遊んでください。あなたがそれを手に入れたら、読んでください...
あなたの混乱の一部は、foldl
andfoldr
が常にどちらか一方の側から始まるということだと思います。したがって、長さを指定する必要はありません。
foldr
非常に単純な定義を持っています
foldr _ z [] = z
foldr f z (x:xs) = f x $ foldr f z xs
なぜこれが無限リストで終了するのでしょうか、よく試してください
dumbFunc :: a -> b -> String
dumbFunc _ _ = "always returns the same string"
testFold = foldr dumbFunc 0 [1..]
ここではfoldr
、"" (値は重要ではないため) と自然数の無限リストに渡します。これは終了しますか?はい。
終了する理由は、Haskell の評価が遅延項書き換えに相当するためです。
そう
testFold = foldr dumbFunc "" [1..]
になります (パターン マッチングを可能にするため)
testFold = foldr dumbFunc "" (1:[2..])
これは (フォールドの定義から) と同じです。
testFold = dumbFunc 1 $ foldr dumbFunc "" [2..]
の定義により、次のdumbFunc
ように結論付けることができます。
testFold = "always returns the same string"
これは、何かを実行するが、時には遅延する関数がある場合に、より興味深いものになります。例えば
foldr (||) False
リストにTrue
要素が含まれているかどうかを調べるために使用されます。これを使用して、渡された関数がリストのいくつかの要素に対して true である場合にのみany
返す高階関数 n を定義できます。True
any :: (a -> Bool) -> [a] -> Bool
any f = (foldr (||) False) . (map f)
遅延評価の良いところは、最初の要素に遭遇すると停止することe
です。f e == True
一方、これは には当てはまりませんfoldl
。なんで?まあ、本当に単純なfoldl
ように見えます
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
さて、上記の例を試してみたらどうなるでしょう
testFold' = foldl dumbFunc "" [1..]
testFold' = foldl dumbFunc "" (1:[2..])
これは次のようになります。
testFold' = foldl dumbFunc (dumbFunc "" 1) [2..]
それで
testFold' = foldl dumbFunc (dumbFunc (dumbFunc "" 1) 2) [3..]
testFold' = foldl dumbFunc (dumbFunc (dumbFunc (dumbFunc "" 1) 2) 3) [4..]
testFold' = foldl dumbFunc (dumbFunc (dumbFunc (dumbFunc (dumbFunc "" 1) 2) 3) 4) [5..]
などなど。Haskell は常に最も外側の関数を最初に評価するため、どこにもたどり着くことはできません (つまり、簡単に言えば遅延評価です)。
これの素晴らしい結果の 1 つは、foldl
out を実装できますがfoldr
、その逆はできないということです。これは、他のほとんどすべてを実装するために使用するものであるため、何らかの深遠な意味でfoldr
、すべての高階文字列関数の中で最も基本的なものであることを意味します。tail を再帰的に実装し、そこからパフォーマンスを向上 させることができるfoldl
ため、時々使用したい場合があります。foldl