-2

首先,只是一些快速的上下文。我正在阅读Haskell Programming From First Principles这本书,并遇到了以下练习。

尝试编写一个解析器,它可以做什么string,但使用char.

我想不通,所以我检查了实现的源代码。我目前正试图绕过它。这里是:

class Parsing m => CharParsing m where
    -- etc.
    string :: CharParsing m => String -> m String
    string s = s <$ try (traverse_ char s) <?> show s

我的问题如下,从最具体到最不具体。

  1. 为什么是show必要的?

  2. 为什么是s <$必要的?工作不traverse char s <?> s一样吗?换句话说,为什么我们要把遍历的结果扔掉呢?

  3. 遍历发生了什么?我得到了列表遍历的作用,所以我想我对 Parser 的 Applicative/Monad 实例感到困惑。在高层次上,我知道遍历 applychar具有 type CharParsing m => Char -> m Char,适用于 string 中的每个字符s,然后将所有结果收集到 typeParser [Char]中。所以类型是有道理的,但我不知道后台发生了什么。

提前致谢!

4

1 回答 1

4

1)为什么是show必要的?

因为showing 字符串(或 aText等)会转义特殊字符,这对错误消息很有意义:

GHCi> import Text.Parsec -- Simulating your scenario with Parsec.
GHCi> runParser ((\s -> s <$ try (traverse_ char s) <?> s) "foo\nbar") () "" "foo"
Left (line 1, column 4):
unexpected end of input
expecting foo
bar
GHCi> runParser ((\s -> s <$ try (traverse_ char s) <?> show s) "foo\nbar") () "" "foo"
Left (line 1, column 4):
unexpected end of input
expecting "foo\nbar"

2)为什么是s <$必要的?遍历char s <?> s工作不一样吗?换句话说,为什么我们要把遍历的结果扔掉呢?

解析的结果是不必要的,因为我们事先知道它会是s(如果解析成功)。traverse将不必要地s从解析每个单独字符的结果中重建。一般来说,如果不需要结果,最好使用traverse_(它只是组合效果,丢弃结果而不尝试重建数据结构)而不是traverse,所以这可能是函数按原样编写的原因.

3)遍历发生了什么?

traverse_ char s( traverse_, 而不是traverse, 如上所述) 是一个解析器。它尝试按顺序解析 中的每个字符s,同时丢弃结果,它是通过对 中的每个字符进行排序的解析器构建的s。提醒一下这traverse_只是一个使用的折叠(*>)可能会有所帮助:

-- Slightly paraphrasing the definition in Data.Foldable:
traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
traverse_ f = foldr (\x u -> f x *> u) (pure ())
于 2017-05-19T05:47:38.187 回答