2013-01-07 60 views
0

我与Haskell的初学者,所以它可能是很明显的,我做错了什么......想不通负前瞻与秒差距

虽然试图解析"1:1,2, 2:18, 3:100"[(1,1), (1,2), (2,18), (3,100)]我被困在一个超前。

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

问题在于最后一个函数verseNr,如果没有冒号后面的话,它应该解析+使用该数字,否则失败而不消耗任何东西(将数字作为章节号码解析为refGroupByChapter)。

除了这个问题似乎很好地工作:)

import Text.ParserCombinators.Parsec 

main = do 
    case (parse refString "(unknown)" "1:1,2, 2:18, 3:100") of 
    Left e -> do putStr "parse error at "; print e 
    Right x -> print x -- expecting: [(1,1), (1,2), (2,18), (3,100)] 

refString :: GenParser Char st [(Int, Int)] 
refString = do 
    refGroups <- many refGroupByChapter 
    eof 
    return $ concat $ map flatten refGroups 
    where flatten (_, []) = [] 
     flatten (c, v:vs) = (c, v):(flatten (c, vs)) 

refGroupByChapter :: GenParser Char st (Int, [Int]) 
refGroupByChapter = do 
    chapterNum <- many digit 
    char ':' 
    verseNums <- verseNrs 
    return ((read chapterNum :: Int), verseNums) 

verseNrs :: GenParser Char st [Int] 
verseNrs = do 
    first <- verseNr 
    remaining <- remainingVerseNrs 
    return (first:remaining) 
    where 
    remainingVerseNrs = do -- allow for spaces around the commas 
     (spaces >> oneOf "," >> spaces >> verseNrs) <|> (return []) 
    verseNr = try $ do 
     n <- many1 digit 
     notFollowedBy $ char ':' -- if followed by a ':' it's a chapter number 
     return (read n :: Int) 

回答

1

诀窍为您的特定问题将是使用sepBy家庭的功能。您正在解析用逗号分隔的数字列表,这正是sepBy的用途。经文列表具有以下属性:必须至少有一个经文编号,并有一个尾随逗号。结合这两者,我们意识到我们需要sepEndBy1函数。这些功能通常用中缀的位置,所以您的代码会是这个样子:

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

我不认为你需要改变任何东西,以获得代码工作。

其他一些小风格的笔记:你有一些不必要的括号。这并不重要,它只是让我感到恼火。例如。在case ... of你做不是需要parens围绕...位。另外,当你使用read时,你不需要类型签名 - 编译器可以推断出类型。也就是说,由于verseNrs返回[Int],编译器和read n生成的Int都完全清楚。没有必要明确地说出来。

+0

非常感谢..它工作得很好,你的风格的评论也发现了我的代码。 – cies

1

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

我相信吉洪的建议是最好的。但是,如果你坚持手动实现它,这里是我将如何做到这一点。

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 [] 

其余的代码是相同的。