学习Haskell`parsec`:尝试重写`words`函数作为基本练习

时间:2014-11-18 05:41:21

标签: haskell parsec

这是一个非常基本的问题,说实话,我觉得有点傻。

TL; DR:如何编写一个函数,利用parsec库来模仿words Data.List函数的行为?预期行为的一个例子:

wordsReplica "I love lamp" = ["I","love","lamp"]

我刚刚从Real World Haskell阅读了Parsec章节的前几页,了解什么构成了一个极小的解析函数(一个不仅仅返回参数或不返回任何内容)将会非常有用。 (RWH的介绍性示例显示了如何解析多行CSV文件...)

因此,我认为使用words重写parsec是一项有用的基本练习......结果证明不是那么基本(对我来说)......

以下是我的尝试;不幸的是,无论我给它什么,它都会产生“意外的输入结束”错误(在运行时)。我已经尝试在haskell.org上的parsec库中阅读简单函数的描述/定义,但它们并不是那种说明性的,至少对于之前从未进行过任何解析的人来说,包括其他语言。

testParser :: String -> Either ParseError [[String]]
testParser input = parse dcParser "(unknown)" input
  where
    wordsReplica = endBy 
                    (sepBy 
                      (many (noneOf " "))
                      (char ' '))
                    (char ' ')

(请原谅lisp-y,非无点免费演示 - 当我学习新函数时,如果我使符号/结构超级明确,它会对我有所帮助。)

更新
这是朝着正确方向迈出的一步(但仍然没有,因为它没有做数字):

λ: let wordsReplica = sepBy (many letter) (char ' ')
λ: parse wordsReplica "i love lamp 867 5309"
Right ["i","love","lamp",""]

更新2:

似乎这个功能可以完成工作,但不确定它是多么惯用:

λ: let wordsReplica = sepBy (many (satisfy(not . isSpace))) (char ' ')
wordsReplica :: Stream s m Char => ParsecT s u m [[Char]]

λ: parse wordsReplica "" "867 5309 i love lamp %all% !(nonblanks are $$captured$$"

Right ["867","5309","i","love","lamp","%all%","!(nonblanks","are","$$captured$$"]
it :: Either ParseError [[Char]]

1 个答案:

答案 0 :(得分:2)

  

更新2:

     

似乎这个功能完成了工作,但不确定它是多么惯用。

没关系,但它并不像你想要的那样有效:

> words "Hello      world"
["Hello","world"]

> parse wordsReplica "" "Hello      world"
Right ["Hello","","","","","","world"]

不完全是你想要的。毕竟,一个单词应至少包含一个字符。但是,如果您将many更改为many1,您会发现另一个错误:

> parse wordsReplicaMany1 "" "Hello      world"
Left (line 1, column 7):
unexpected " "

那是因为你的分离解析器不够贪心。而不是解析单个空间,解析as many as you can

nonSpace      = satisfy $ not . isSpace
wordsReplica' = many1 nonSpace `sepBy` spaces