2013-01-07 58 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 [] 

其餘的代碼是相同的。