gpt4 book ai didi

Haskell 映射不会迭代整个列表

转载 作者:行者123 更新时间:2023-12-02 14:21:39 25 4
gpt4 key购买 nike

我正在尝试学习 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 function ( at line 38 ) — 尽管获取要包含的文件列表 — 只调用第一个元素的函数。这并不是唯一奇怪的行为。被包含的文件还可以有一个被处理的包含 block 并且被引用的文件被包含;但它不会更深入,最后一个文件的包含 block 将被忽略。

为什么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

最佳答案

我可以在您的 doIninclude 函数中发现以下错误:

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],我们可以向后推算:

  1. 结果的类型为IO [Block]
  2. contents 的类型为 [IO [Block]]
  3. msum[IO [Block]] -> IO [Block] 类型一起使用

第三部分就是问题所在——不知何故,在你的程序中,有一个非标准的 MonadPlus 实例正在为 IO 加载,我敢打赌它会做什么msum 内容 是这样的:

  • 执行第一个操作
    • 如果成功,则产生与此相同的结果,并丢弃列表的其余部分。 (这就是您观察到的行为的原因。)
    • 如果因异常而失败,请尝试列表中的其余部分。

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

这里的一般建议是:

  1. 将程序拆分为更小的函数
  2. 为这些函数编写类型签名

因为这里的问题似乎是 msum 所使用的类型与您期望的类型不同。通常这会产生类型错误,但在这里你很不幸,它与某些库中的奇怪类型类实例交互。

<小时/>

根据评论,您使用 msum content 的目的是创建一个 IO 操作,该操作按顺序执行所有子操作,并将其结果收集为列表。嗯,the MonadPlus class isn't normally defined for IO, and when it is it does something else 。因此,此处使用的正确函数是 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]]。要消除双重嵌套列表,只需使用 fmapIO 内应用 concat 即可。

关于Haskell 映射不会迭代整个列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34031192/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com