【问题标题】:Haskell recursion in do block don't work (recursion is in list conprehension)do 块中的 Haskell 递归不起作用(递归在列表理解中)
【发布时间】:2020-05-01 14:24:25
【问题描述】:

我是 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$ 

有人知道如何解决这个问题吗?

【问题讨论】:

  • 您可以使用sequence 函数将操作列表(如[ countFile(file) | file &lt;- files ])转换为执行所有操作的操作

标签: haskell recursion


【解决方案1】:

列表推导是[IO Int] 的表达式,而不是IO Int,而列表[]Monad 类型类的成员,它不是您当前在@987654328 中使用的类型@ 堵塞。您可以在此处使用mapM_ :: (Foldable f, Monad m) =&gt; (a -&gt; m b) -&gt; f a -&gt; 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 says,如果您需要检索结果列表,您应该使用mapM :: (Traversable f, Monad m) =&gt; (a -&gt; m b) -&gt; t a -&gt; m (t b)。因此,您可以提供单子调用的 results 列表。例如,您可以总结这些以获得结果。我把它留作练习。

【讨论】:

  • 我猜 OP 实际上想要mapM(或traverse / for),因为他们真的需要递归调用返回的Ints。
【解决方案2】:

你在IO monad 中,所以每一行都应该产生一个IO a,但是你的列表理解产生了......嗯,一个列表[a]。值得庆幸的是,有一个函数sequence_ :: (Foldable t, Monad m) =&gt; t (m a) -&gt; m (),它将把你的[IO ()]变成一个IO (),同时在其中运行所有的IO操作。因此,您的 else 块应该如下所示:

    else do files <- getDirectoryContents filePath    
            sequence_ [countLines(file) | file <- files]
            pure 0

正如威廉所说,使用mapM_ :: (Foldable f, Monad m) =&gt; (a -&gt; m b) -&gt; f a -&gt; m () 可以进一步简化这些:

    else do files <- getDirectoryContents filePath    
            mapM_ countLines files
            pure 0

【讨论】:

    猜你喜欢
    • 2015-03-10
    • 2017-02-10
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    相关资源
    最近更新 更多