私はそのようなコードを持っています:
foldl (\a x -> a ++ (f x)) [] list
すべて問題ないようですが(f x) :: IO [String]
、(++)
エラーが発生します ([文字列] != IO [文字列])。
空の IO[String] リストを定義するには? クリーン(fx)が作れません。
ここで必要なコンビネータはおそらく
Prelude Control.Monad> :t foldM
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
そしてそれで
foldM (\a x -> fmap (a++) (f x)) [] list
アクションを順番に実行し、それらが生成するf x
を蓄積します。[String]
の:
foldl (\a x -> a ++ (f x)) [] list
あなたはやろうとしていますa ++ f x
がf x :: IO [String]
、それはうまくいきません。何が起こっているのかを理解できるか見てみましょう。
x
for はあなたのから来ることを意味するので、リストの各要素に順番にlist
適用し、それぞれが を与え、進むにつれてそれらを連結したいと考えます。f
[String]
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
関数をリストにマップし、モナド操作を 1 つにまとめて、出力をリストに結合する を使用します。
concatMapM :: (a -> IO [String]) -> [a] -> IO [String]
concatMapM f list = fmap concat (mapM f list)
ここではfmap
、純粋な関数concat
を IO 操作の結果に適用していました。
を使用してテストできます
fileLines :: FilePath -> IO [String]
fileLines filename = fmap lines (readFile filename)
*Main> concatMapM fileLines ["test1.txt","test2.txt"]
["This is test1.txt","It has two lines.","This is test2.txt","It has two lines too."]
concat
この方法は、 よりも効率的に機能するため、foldl を試みるよりも優れていfoldl (++)
ます。これを自分で確認するには、これらの速度を比較してください。
*Main> writeFile "test3.txt" $ foldl (++) [] (map show [1..10000])
*Main> writeFile "test3.txt" $ foldr (++) [] (map show [1..10000])
問題は、リストを4 回foldl
実行することです。最初に追加するときに実行し、次に実行し、次に追加し、次に実行し、追加すると、4 つすべてを実行します。追加します。これは労力の大きな無駄です。(((a++b)++c)++d)++e
a
b
a
b
c
a
b
c
d
e
foldr
そうすることで、各リストをa ++ (b ++ (c ++ (d ++ e)))
1 回実行d
しc
ます。であり、このリスト操作にははるかに適しています。b
a
concat
foldr
次のものがあるとします。
list :: [a]
f :: a -> IO b
type の最終値を取得したいとしますIO [b]
。をマッピングf
するlist
と、 type のリストが得られます[IO b]
。sequence :: Monad m => [m a] -> m [a]
次のように使用して、内側から外側に「IOを移動」できます。
sequence (map f list) :: IO [b]
このように考えてみてください: 順序付けの前に、 type の値を作成するための命令のリストがありましたb
。これで、 のリストを作成する命令が 1 つできましたb
。