当前位置:  开发笔记 > 编程语言 > 正文

Haskell映射不会遍历整个列表

如何解决《Haskell映射不会遍历整个列表》经验,为你挑选了1个好方法。

我正在尝试学习Haskell的基础知识,同时为Pandoc开发一个过滤器,以递归方式包含其他markdown文件.

基于脚本指南,我能够创建一个有点工作的过滤器.这将查找带有include类的CodeBlocks 并尝试包含引用文件的AST.

```include
section-1.md
section-2.md
#pleasedontincludeme.md
```

整个过滤器和输入源可以在以下存储库中找到:steindani/pandoc-include(或见下文)

可以使用过滤器运行pandoc,并使用以下命令以markdown格式查看输出: pandoc -t json input.md | runhaskell IncludeFilter.hs | pandoc --from json --to markdown

我注意到map函数(在第38行) - 尽管获取要包含的文件列表 - 只调用第一个元素的函数.这不是唯一奇怪的行为.包含的文件也可以包含一个处理的包含块,并包含引用的文件; 但它不会更深入,忽略最后一个文件的包含块.

为什么map函数不遍历整个列表?为什么它在2级层次结构后停止?

请注意,我刚刚开始学习Haskell,我确信我犯了错误,但我很高兴学习.

谢谢

完整源代码:

module Text.Pandoc.Include where

import Control.Monad
import Data.List.Split

import Text.Pandoc.JSON
import Text.Pandoc
import Text.Pandoc.Error

stripPandoc :: Either PandocError Pandoc -> [Block]
stripPandoc p =
  case p of
    Left _ -> [Null]
    Right (Pandoc _ blocks) -> blocks

ioReadMarkdown :: String -> IO(Either PandocError Pandoc)
ioReadMarkdown content = return (readMarkdown def content)

getContent :: String -> IO [Block]
getContent file = do
  c <- readFile file
  p <- ioReadMarkdown c
  return (stripPandoc p)

doInclude :: Block -> IO [Block]
doInclude cb@(CodeBlock (_, classes, _) list) =
  if "include" `elem` classes
    then do
      files <- return $ wordsBy (=='\n') list
      contents <- return $ map getContent files
      result <- return $ msum contents
      result
    else
        return [cb]
doInclude x = return [x]

main :: IO ()
main = toJSONFilter doInclude

Luis Casilla.. 6

我可以在你的doInclude函数中发现以下错误:

doInclude :: Block -> IO [Block]
doInclude cb@(CodeBlock (_, classes, _) list) =
  if "include" `elem` classes
    then do
      let files = wordsBy (=='\n') list
      let contents = map getContent files
      let result = msum contents            -- HERE
      result 
    else
        return [cb]
doInclude x = return [x]

由于整个函数的结果类型是IO [Block],我们可以向后工作:

    result 有类型 IO [Block]

    contents 有类型 [IO [Block]]

    msum 正在使用类型 [IO [Block]] -> IO [Block]

而第三部分是问题 - 在你的程序中,有一个非标准的MonadPlus实例被加载IO,我打赌它的作用msum contents是:

执行第一个动作

如果成功,产生与此相同的结果并丢弃列表的其余部分.(这是您观察到的行为的原因.)

如果失败并出现异常,请尝试列表的其余部分.

这不是标准MonadPlus实例,因此它来自您要导入的其中一个库.我不知道哪个.

这里的一般建议是:

    将程序拆分为较小的功能

    为这些函数写入类型签名

因为这里的问题似乎msum是使用与您期望的类型不同的类型.通常这会产生类型错误,但在这里你运气不好,它与某个库中的奇怪类型类实例进行交互.


从评论中,您的意图msum contents是创建一个IO按顺序执行所有子动作的动作,并将其结果作为列表收集.好吧,这个MonadPlus类通常IO不是为它定义的,当它正在做的时候.所以这里使用的正确函数是sequence:

-- Simplified version, the real one is more general:
sequence :: Monad m => [m a] -> m [a]
sequence [] = return []
sequence (ma:mas) = do
  a <- ma
  as <- mas
  return (a:as)

这让你从[IO [Block]]IO [[Block]].要消除双嵌套列表,您只需使用内部fmap应用.concatIO



1> Luis Casilla..:

我可以在你的doInclude函数中发现以下错误:

doInclude :: Block -> IO [Block]
doInclude cb@(CodeBlock (_, classes, _) list) =
  if "include" `elem` classes
    then do
      let files = wordsBy (=='\n') list
      let contents = map getContent files
      let result = msum contents            -- HERE
      result 
    else
        return [cb]
doInclude x = return [x]

由于整个函数的结果类型是IO [Block],我们可以向后工作:

    result 有类型 IO [Block]

    contents 有类型 [IO [Block]]

    msum 正在使用类型 [IO [Block]] -> IO [Block]

而第三部分是问题 - 在你的程序中,有一个非标准的MonadPlus实例被加载IO,我打赌它的作用msum contents是:

执行第一个动作

如果成功,产生与此相同的结果并丢弃列表的其余部分.(这是您观察到的行为的原因.)

如果失败并出现异常,请尝试列表的其余部分.

这不是标准MonadPlus实例,因此它来自您要导入的其中一个库.我不知道哪个.

这里的一般建议是:

    将程序拆分为较小的功能

    为这些函数写入类型签名

因为这里的问题似乎msum是使用与您期望的类型不同的类型.通常这会产生类型错误,但在这里你运气不好,它与某个库中的奇怪类型类实例进行交互.


从评论中,您的意图msum contents是创建一个IO按顺序执行所有子动作的动作,并将其结果作为列表收集.好吧,这个MonadPlus类通常IO不是为它定义的,当它正在做的时候.所以这里使用的正确函数是sequence:

-- Simplified version, the real one is more general:
sequence :: Monad m => [m a] -> m [a]
sequence [] = return []
sequence (ma:mas) = do
  a <- ma
  as <- mas
  return (a:as)

这让你从[IO [Block]]IO [[Block]].要消除双嵌套列表,您只需使用内部fmap应用.concatIO

推荐阅读
郑谊099_448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有