我正在学习Haskell,我今天的目标是编写一个函数sizeOf :: FilePath -> IO Integer(计算文件或文件夹的大小),用逻辑
path是文件,System.Directory.getFileSize pathpath是目录,获取其内容列表,递归运行此函数,以及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]]......它将它们包装在一个列表中.我想这就是发生在我身上的事情,但我已经走出了我的深度.
你需要
sum <$> (listDirectory path >>= mapM sizeOf)
Run Code Online (Sandbox Code Playgroud)
说明:
sumover的想法IO [Integer]是可以的,所以我们需要得到这样的东西.listDirectory path给了我们IO [FilePath],所以我们需要传递每个文件路径sizeOf.这就是>>=一起mapM做的事情.map仅此一点就会给我们[IO Integer]这就是我们需要的原因mapM