使用Haskell迭代文件

atl*_*tis 2 directory haskell file

我有一个Haskell函数,它在单个文件上运行以生成映射.我想迭代目录中的所有文件并应用此函数来生成单个映射.

我试图这样做:

perFileFunc :: Int -> FilePath -> IO (Map.Map [Char] Double)

allFilesIn dir =  filter (/= "..")<$>(filter(/= ".")<$>(getDirectoryContents dir)
Run Code Online (Sandbox Code Playgroud)

这给了我一个目录中所有文件名的列表,除了.和..

现在,当我尝试做

myFunc dir n = map (perFileFunc n) <$> allFilesIn dir
Run Code Online (Sandbox Code Playgroud)

它没有做任何事情.我期待一个地图列表,我可能会使用unionWith(+)加入.

这似乎不是正确的方法.

ehi*_*ird 6

你的代码不能正常工作,因为它(<$>)是为了将纯粹的动作提升到一个monadic(实际应用的)上下文中,所以你myFunc dir n的类型是IO [IO (Map.Map [Char] Double)]; 一个IO操作,在执行时,查找目录中的文件列表,并将每个操作映射到另一个IO操作,该操作在执行时生成Map您想要的操作 - 而不实际执行任何操作.那可能不是你想要的:)

您希望执行一个函数,在列表的每个元素上返回一个monadic操作,并返回结果值的列表.这就是mapM所做的:

mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
Run Code Online (Sandbox Code Playgroud)

所以你真正想要的是:

myFunc dir n = allFilesIn dir >>= mapM (perFileFunc n)
Run Code Online (Sandbox Code Playgroud)

你使用(>>=)因为allFilesIn dir是monadic动作本身,并且你想将它传递给期望其结果类型并返回另一个动作的函数(在这种情况下,mapM).

请注意,mapM与此不同map的是,IO(并非每个monad的行为都像这样,但大多数都是这样),它会在返回列表之前执行每个动作; 这意味着每个操作的结果必须共同适合内存,并且您将无法以递增方式处理结果.如果你想要,你需要的东西mapM,比如iteratees.