我是Haskell的初学者,所以我做错了可能很明显......
在尝试将"1:1,2, 2:18, 3:100"
解析为[(1,1), (1,2), (2,18), (3,100)]
时,我陷入了前瞻。
要知道一个数字是否是一个经文数字,它应该向前看一个冒号,因为那时它是一个章节号。
问题在于最后一个函数verseNr
,它应该解析+消耗数字,如果没有后跟冒号,否则失败而不消耗任何东西(留下号码作为章节号解析{{1 }})。
除了这个问题,它似乎运作良好:)
refGroupByChapter
答案 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 []
其余代码是相同的。