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)
4

2 に答える 2

1

2 つの問題があります。まず、verseNr数値の後に:. 関数は常に、が として数字に対してパターン マッチングによって数字を解析することに成功するverseNrsと想定します。第 2 に、この関数は文字列の最後の数字の大文字と小文字の区別を処理しません。verseNrfirstverseNrs,

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

コードの残りの部分は同じです。

于 2013-01-07T15:13:31.090 に答える
1

特定の問題の秘訣はsepBy、関数のファミリを使用することです。コンマで区切られた数値のリストを解析していますが、これはまさにそのsepBy目的です。節のリストには、次のプロパティがあります。少なくとも 1 つの節番号が必要であり、末尾にコンマが必要です。2つを組み合わせると、sepEndBy1関数が必要であることがわかります。これらの関数は通常、中置位置に記述されるため、コードは次のようになります。

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

コードを機能させるために他に何かを変更する必要はないと思います。

他のいくつかのマイナーなスタイル ノート: 不要な括弧がいくつかあります。これは重要ではありません。個人的に私を悩ませているだけです。たとえば、ビットの周りに括弧は必要ありcase ... ofません。...また、 -- を使用する場合、型シグネチャは必要ありませんread。コンパイラは型を推測できます。つまり、 がverseNrs返さ[Int]れるので、コンパイラにとっても私にとってもread nInt. はっきりと言う必要はありません。

于 2013-01-07T04:03:38.353 に答える