使用Haskell迭代文件

时间:2011-12-20 06:57:19

标签: file haskell directory

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

我试图以这种方式接近它:

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

allFilesIn dir =  filter (/= "..")<$>(filter(/= ".")<$>(getDirectoryContents dir)

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

现在我试着做

myFunc dir n = map (perFileFunc n) <$> allFilesIn dir

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

这似乎不是正确的方法。

2 个答案:

答案 0 :(得分:6)

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

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

Map

所以你真正想要的是:

mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

您使用myFunc dir n = allFilesIn dir >>= mapM (perFileFunc n) 因为(>>=)本身就是一个monadic动作,并且您希望将其传递给期望其结果类型并返回另一个动作的函数(在本例中为allFilesIn dir

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

答案 1 :(得分:2)

了解Haskell的棘手问题是如何识别和组合IO操作。我们来看一些类型的签名。

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

现在,你说myFunc

  

我期待一张地图列表

因此,对于该功能,您需要类型签名

myFunc :: Int -> FilePath -> [Map.Map String Double]

当然,返回类型不能只是 [Map.Map String Double],因为我们需要执行一些IO才能评估myFunc。因此,给定IntFilePath,我们实际上希望返回类型为 IO动作生成 a [Map.Map String Double]

myFunc :: Int -> FilePath -> IO [Map.Map String Double]

现在,让我们看一下我们将要创建此功能的IO动作。

allFilesIn dir :: IO [FilePath]
perFileFunc n  :: FilePath -> IO (Map.Map String Double)

perFileFunc实际上不是IO操作,但一个函数,给定FilePath会产生IO操作。所以,让我们看看......如果我们运行allFilesIn操作,那么我们就可以使用该列表并在每个元素上运行perFileFunc n

myFunc dir n = do
  files <- allFilesIn dir
  ???

那么???点的内容是什么?我们可以使用[FilePath],因为我们使用<-来运行操作allFilesIn dir。我们有一个函数perFileFunc n :: FilePath -> IO (Map.Map String Double)。我们希望结果的类型为IO [Map.Map String Double]

停止...... Hoogle时间!推广我们所拥有的组件(a = FilePathb = Map.Map String Double),我们hoogle for [a] -> (a -> IO b) -> IO [b](假装我们还没有看到ehird的答案)。瞧,mapM是我们正在寻找的神奇解决方案! (或forM,只是flip mapM

myFunc dir n = do
  files <- allFilesIn dir
  mapM (perFileFunc n) files

如果你发现这个符号,你会发现它减少到了第三个回答:

myFunc dir n = allFilesIn dir >>= (\files -> mapM (perFileFunc n) files)
-- eta reduce (\x -> f x) ==> f
myFunc dir n = allFilesIn dir >>= mapM (perFileFunc n)