首页 > 解决方案 > do 块中的 Haskell 递归不起作用(递归在列表理解中)

问题描述

我对 Haskell 很陌生,想在 do 块中执行递归。

countLines :: String -> IO Int
countLines filePath = do
    isFile <- doesFileExist filePath
    if isFile
        then do contents <- readFile filePath
                print contents
                pure 0
        else do files <- getDirectoryContents filePath    
                [countLines(file) | file <- files] -- recursion here!
                pure 0

在我添加这个列表理解之前,一切正常,但是一旦我添加这个,我得到以下错误:

Main.hs:59:17: error:
    • Couldn't match type ‘[]’ with ‘IO’
      Expected type: IO (IO Int)
        Actual type: [IO Int]
    • In a stmt of a 'do' block: [countLines (file) | file <- files]
      In the expression:
        do files <- getDirectoryContents filePath
           [countLines (file) | file <- files]
           pure 0
      In a stmt of a 'do' block:
        if isFile then
            do contents <- readFile filePath
               print contents
               pure 0
        else
            do files <- getDirectoryContents filePath
               [countLines (file) | file <- files]
               pure 0
   |
59 |                 [countLines(file) | file <- files]
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
bash-3.2$ 

有谁知道如何解决这一问题?

标签: haskellrecursion

解决方案


列表推导是[IO Int], not的表达式IO Int,虽然列表[]是类型类的成员Monad,但它不是您当前在do块中使用的。您可以mapM_ :: (Foldable f, Monad m) => (a -> m b) -> f a -> m ()在这里使用递归:

countLines :: String -> IO Int
countLines filePath = do
    isFile <- doesFileExist filePath
    if isFile
        then readFile filePath >>= print
        else getDirectoryContents filePath >>= mapM_ countLines
    pure 0

正如@chi 所说,如果您需要检索结果列表,您应该使用mapM :: (Traversable f, Monad m) => (a -> m b) -> t a -> m (t b). 因此,您可以提供单子调用结果的列表。例如,您可以将这些相加以获得结果。我把它留作练习。


推荐阅读