无法用Parsec弄清楚负面的前瞻

时间:2013-01-07 01:44:14

标签: haskell parsec lookahead

我是Haskell的初学者,所以我做错了可能很明显......

在尝试将"1:1,2, 2:18, 3:100"解析为[(1,1), (1,2), (2,18), (3,100)]时,我陷入了前瞻。

要知道一个数字是否是一个经文数字,它应该向前看一个冒号,因为那时它是一个章节号。

问题在于最后一个函数verseNr,它应该解析+消耗数字,如果没有后跟冒号,否则失败而不消耗任何东西(留下号码作为章节号解析{{1 }})。

除了这个问题,它似乎运作良好:)

refGroupByChapter

2 个答案:

答案 0 :(得分:1)

您特定问题的诀窍是使用sepBy系列函数。您正在解析用逗号分隔的数字列表,这正是sepBy的用途。经文列表具有以下属性:必须至少有一个经文编号,并且有一个尾随逗号。结合这两者,我们意识到我们需要sepEndBy1函数。这些函数通常写在中缀位置,因此您的代码看起来像这样:

verseNrs = verseNr `sepEndBy1` (spaces >> char ',' >> spaces)

我认为您不需要更改任何其他内容以使代码生效。

其他一些小调风格:你有一些不必要的括号。这并不重要,它只是让我个人很烦。例如。在case ... of中你需要围绕...位的parens。此外,使用read时不需要类型签名 - 编译器可以推断类型。也就是说,由于verseNrs返回[Int],因此编译器和我read n生成Int的内容完全清楚。没有必要明确说出来。

答案 1 :(得分:1)

有两个问题。首先,函数verseNr在解析数字时可能并不总是成功,因为数字可能后跟:。虽然verseNrs函数始终假定verseNr成功通过模式匹配解析数字first。其次,函数verseNrs不处理字符串中最后一位数的情况,后面跟不是,

我相信Tikhon的建议是最好的。但是,如果你坚持手动实现它,我就是这样做的。

import Control.Monad (void)
import Control.Applicative ((<*))

verseNrs :: GenParser Char st [Int]
verseNrs = do
    first <- fmap Just (try (many1 digit
                          <* spaces
                          <* (eof <|> void (char ','))
                          <* spaces))
             <|> return Nothing
    case first of
      Just first -> fmap (read first:) verseNrs
      Nothing    -> return []

其余代码是相同的。