このHaskellコードが無限リストで正常に機能するのはなぜですか?
-
08-07-2019 - |
質問
無限リストで正しく動作するコードがいくつかありますが、なぜ正常に動作するかはわかりません。 (無限リストを処理しない元のコードを変更して、他のコードから何かをオンラインに組み込むと、突然動作することがわかりましたが、理由はわかりません)。
myAny :: (a -> Bool) -> [a] -> Bool
myAny p list = foldr step False list
where
step item acc = p item || acc
folderの私の理解は、リスト内のすべての項目をループすることです(そして、おそらく理解が不完全です)。その場合、「ステップ」がどのように問題にならないか関数が表現されています...コードは無限ループを処理できないはずです。
ただし、次のように機能します:
*Main Data.List> myAny even [1..]
True
理解を助けてください:なぜ??
解決
Haskellがあなたの表現をどのように評価するかについて、少しトレースしてみましょう。各行の等号を等号に置き換えると、式は非常に迅速にTrueと評価されます。
myAny even [1..]
foldr step False [1..]
step 1 (foldr step False [2..])
even 1 || (foldr step False [2..])
False || (foldr step False [2..])
foldr step False [2..]
step 2 (foldr step False [3..])
even 2 || (foldr step False [3..])
True || (foldr step false [3..])
True
これは、 acc
が未評価のサンク(遅延評価)として渡されるため機能しますが、 ||
関数は first 引数。
これで終了します:
True || and (repeat True)
しかし、これはそうではありません:
and (repeat True) || True
||の定義を見てくださいなぜそうなのかを見るには:
True || _ = True
False || x = x
他のヒント
folderの私の理解は、 内のすべてのアイテムをループします リスト(そしておそらくその理解 不完全です)。
folder
は( foldl
とは異なり)リストのすべての項目をループする必要はありません。 folder
がどのように定義されているかを調べることは有益です。
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
folder
の呼び出しが評価されると、関数 f
の呼び出しの評価が強制されます。ただし、 folder
の再帰呼び出しが、関数 f
の引数にどのように埋め込まれるかに注意してください。 f
が2番目の引数を評価しない場合、その再帰呼び出しは評価されません。
ここで重要なのは、Haskellが非厳密な言語であることです。 "非厳格な"は、厳密でない関数を許可することを意味します。つまり、関数パラメーターは、使用される前に完全に評価されない可能性があります。これにより、明らかに遅延評価が可能になります。これは、「結果が必要になるまで計算を遅らせる手法」です。
から開始私はHaskellを知りませんが、あなたの場合、遅延評価のために動作すると思われます。リストを無限に操作できるため、リストにアクセスすると、必要に応じて結果が計算されます。