てみましょう慎重ので、チェックを入力しないであろうcategorize
hashFile :: FilePath -> IO (Digest MD5) -- I simplified the MonadIO constraint
categorize :: Ord k => ( a -> k ) -> [a] -> M.Map k [a]
categorize hashFile
に引数としてhashFile
を渡すために私たちが望むことを念頭に、categorize
の種類とhashFile
のタイプを比較しGHC k
をIO (Digest MD5)
に一致させようとしますが、IO
にはOrd
インスタンスがありません。言い換えれば、IO (Digest MD5)
はMap
の鍵として役に立たない:あなたが必要とするのはDigest MD5
であり、実行すると最終的にはDigest MD5
を生成する計算ではない。
IO
の計算をすべて実行し、その結果(タイプDigest MD5
)をMap
に入力してください。結果の関数はIO (Map (Digest MD5) FilePath)
- IO
の計算を返し、実行するとMap (Digest MD5) FilePath
を返します。
これを行う最も簡単な方法は、必要なタイプに合わせてcategorize
を調整することです。
categorize :: (Applicative f, Ord k) => (a -> f k) -> [a] -> f (M.Map k a)
categorize f = fmap M.fromList . traverse (\x -> fmap (, x) (f x))
(私はTupleSections
を使用しています)まずタイプを見てみましょう。 IO
がApplicative
のインスタンスであるので、(a -> f k)
は、以下の制約の下でFilePath -> IO (Digest MD5)
で統一:私たちが望むタイプです
a ~ FilePath
f ~ IO
k ~ Digest MD5
のでcategorize hashFile :: [FilePath] -> IO (M.Map (Digest MD5) FilePath)
、。
ここで実装を見てみましょう。 traverse :: Applicative f => (a -> f b) -> [a] -> f [b]
*(nee mapM
)はApplicative
関数を受け取り、それをリストにマップし、結果をまとめてリストにします。各アイテムを(result, item)
タプルに変換するために使用しています。これによりf [(k, a)]
値が生成されます。そして、 M.fromList
の結果がf (M.Map k a)
になります。
*技術的には、traverse
は、より一般的なタイプの(Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)
です。私はt ~ []
を取ってこのバージョンに着いた。
この実装ではM.fromList
が使用されるため、重複した項目が破棄されます。実際には、ファイルの内容が同じであることを期待していない場合は、MD5のハッシュが異なるため問題ありません。練習問題:重複を維持したい場合、これはどのように変化しますか?
1.あなたは 'FilePath - > IO(Digest MD5)'を持っています。 '[IO(Digest MD5、FilePath)]'リストを構築する// 3. Traversableクラスを使って 'IO [(FilePath) [Digest MD5(FilePath)]) – hao