ディレクトリのすべての内容を幅優先順でリストすると効率が低下するという質問で、効率が低いのは再帰モナド関数の奇妙な動作によるものであることがわかりました。
試す
sequence $ map return [1..]::[[Int]]
sequence $ map return [1..]::Maybe [Int]
そしてghciは無限の計算に陥ります。
次のように、より読みやすい形式でシーケンス関数を書き直すと:
sequence' [] = return []
sequence' (m:ms) = do {x<-m; xs<-sequence' ms; return (x:xs)}
そして試してください:
sequence' $ map return [1..]::[[Int]]
sequence' $ map return [1..]::Maybe [Int]
同じ状況、無限ループが発生します。
有限リストを試す
sequence' $ map return [1..]::Maybe [Int]
Just [1,2,3,4..]
長い間待った後、期待される結果が飛び出します。
私たちが試したことから、sequence' の定義は怠惰に見えますが、厳密であり、sequence' の結果を出力する前にすべての数値を作成する必要があるという結論に達することができます。
シーケンスだけでなく、関数を定義すると
iterateM:: Monad m => (a -> m a) -> a -> m [a]
iterateM f x = (f x) >>= iterateM0 f >>= return.(x:)
そして試してみてください
iterateM (>>=(+1)) 0
その後、無限の計算が発生します。
ご存知のように、非モナド iterate は上記の iterateM と同じように定義されますが、なぜ iterate が遅延で iterateM が厳密なのかを説明します。上記からわかるように、 iterateM と sequence' はどちらも再帰的なモナド関数です.再帰的なモナド関数で奇妙なことはありますか?