在寻找目录内容大小时,IO会遇到什么困难?

Gre*_*gle 5 haskell

我正在学习Haskell,我今天的目标是编写一个函数sizeOf :: FilePath -> IO Integer(计算文件或文件夹的大小),用逻辑

  • 如果path是文件,System.Directory.getFileSize path
  • 如果path是目录,获取其内容列表,递归运行此函数,以及sum结果
  • 如果它不是文件或目录, return 0

这是我在Ruby中实现它的方式,以说明(Ruby注意:map的参数相当于\d -> size_of d,reduce :+foldl (+) 0,任何函数结束都?返回一个bool,返回是隐式的):

def size_of path
  if File.file? path
    File.size path
  elsif File.directory? path
    Dir.glob(path + '/*').map { |d| size_of d }.reduce :+
  end
end
Run Code Online (Sandbox Code Playgroud)

这是我在Haskell的解决方案:

  sizeOf :: FilePath -> IO Integer
  sizeOf path =
    do
      isFile <- doesFileExist path
      if isFile then
        getFileSize path
      else do
        isDir <- doesDirectoryExist path
        if isDir then
          sum $ map sizeOf $ listDirectory path
        else
          return 0
Run Code Online (Sandbox Code Playgroud)

我知道我的问题在哪里.sum $ map sizeOf $ listDirectory path,listDirectory path返回a IO [FilePath]而不是a FilePath.但是......我无法想象任何解决这个问题的解决方案.<$>而不是$第一个想到的东西,因为<$>我理解是让某个功能a -> b成为一个东西的东西Context a -> Context b.但是......我猜IO不是那样的?

我花了大约两个小时对那里的逻辑感到困惑.我在其他例子上尝试过.这是一个相关的发现,告诉我:如果double = (*) 2,然后map double [1,2,3] == [2,4,6],但map double <$> [return 1, return 2, return 3] == [[2],[4],[6]]......它将它们包装在一个列表中.我想这就是发生在我身上的事情,但我已经走出了我的深度.

Ing*_*ngo 5

你需要

sum <$> (listDirectory path >>= mapM sizeOf)
Run Code Online (Sandbox Code Playgroud)

说明:

  • 使用sumover的想法IO [Integer]是可以的,所以我们需要得到这样的东西.
  • listDirectory path给了我们IO [FilePath],所以我们需要传递每个文件路径sizeOf.这就是>>=一起mapM做的事情.
  • 请注意,map仅此一点就会给我们[IO Integer]这就是我们需要的原因mapM