1

私はやや複雑なデータ ファイル形式を処理するために Parsec パーサーに取り組んでいます (この形式を制御することはできません)。

私は多くの進歩を遂げましたが、現在、次のことで立ち往生しています。

次のような行を解析できる必要があります。

4  0.123  1.452  0.667  *  3.460  149 - -

意味的には、4は nodeNum であり、Floats*は負の対数確率です (したがって、*確率ゼロの負の対数を表します)。と149マイナス記号は本当に迷惑なので破棄しても構いませんが、少なくともパーサーを壊さないようにする必要があります。

これが私がこれまでに持っているものです:

これは、私が言及した「ジャンク」を処理します。おそらくもっと単純かもしれませんが、それ自体で機能します。

 emAnnotationSet = (,,) <$> p_int  <*>
                           (reqSpaces *> char '-') <*>
                           (reqSpaces *> char '-')

行のnodeNum先頭にある は、機能する別のパーサーによって処理されるため、私が入る必要はありません。

問題はp_logProbemAnnotationSet.

のパーサーはp_logProb次のようになります。

p_logProb = liftA mkScore (lp <?> "logProb")
          where lp = try dub <|> string "*"
                dub = (++) <$> ((++) <$> many1 digit <*> string ".") <*> many1 digit

そして最後に、次のようにlogProbエントリを末尾emAnnotationSet(整数で始まる) から分離しようとします。

hmmMatchEmissions     = optSpaces *> (V.fromList <$> sepBy p_logProb reqSpaces) 
                      <* optSpaces <* emAnnotationSet <* eol 
                      <?> "matchEmissions"

そのp_logProbため、数字で始まり、小数点を含み、さらに数字を持つ float でのみ成功します (この制限はファイル形式によって尊重されます)。

小数点以下を解析しない場合try、定義内のが先頭の数字を消費しないようにすることを望んでいましたが、これは機能していないようです。p_logProbParsec は、その整数の数字の後に予期しないスペースがあるとまだ不平を言っていますemAnnotationSet

Left "hmmNode" (line 1, column 196):
unexpected " "
expecting logProb

列 196 は、マイナス記号の前の整数の後のスペースに対応しているため、p_logProbパーサーが整数を消費していることに問題があることは明らかです。p_logProbパーサーが先読みを正しく使用して、その入力をパーサーに残すようにするにはどうすればよいemAnnotationSetですか?

4

2 に答える 2

2

確率を終了する整数は、小数点を含まないため、確率と間違えることはありません。コンビネータはlexeme、パーサーを末尾のスペースをスキップするパーサーに変換します。

import Text.Parsec
import Text.Parsec.String
import Data.Char
import Control.Applicative ( (<$>), (<*>), (<$), (<*), (*>) )

fractional :: Fractional a => Parser a
fractional = try $ do
  n <- fromIntegral <$> decimal
  char '.'
  f <- foldr (\d f -> (f + fromIntegral (digitToInt d))/10.0) 0.0 <$> many1 digit  
  return $ n + f

decimal :: Parser Int
decimal = foldl (\n d -> 10 * n + digitToInt d) 0 <$> many1 digit

lexeme :: Parser a -> Parser a
lexeme p = p <* skipMany (char ' ')

data Row = Row Int [Maybe Double]
           deriving ( Show )

probability :: Fractional a => Parser (Maybe a)
probability = (Just <$> fractional) <|> (Nothing <$ char '*')

junk = lexeme decimal <* count 2 (lexeme $ char '-')

row :: Parser Row
row = Row <$> lexeme decimal <*> many1 (lexeme probability) <* junk

rows :: Parser [Row]
rows = spaces *> sepEndBy row (lexeme newline) <* eof

使用法:

*Main> parseTest rows "4 0.123 1.234 2.345 149 - -\n5 0.123 * 2.345 149 - -" 
[Row 4 [Just 0.123,Just 1.234,Just 2.345],Row 5 [Just 0.123,Nothing,Just 2.345]]
于 2012-05-25T08:40:06.553 に答える
1

あなたの問題について正確にはわかりません。ただし、説明に基づいて指定された行を解析するにはText.Parsec.Token1、 で定義されている既存のレクサーを使用してそれらを結合する方がはるかに簡単です。

以下のコードは、行を Line データ型に解析します。必要に応じて、そこからさらに処理できます。-解析する前に と 整数を除外しようとする代わりに、 、 、および整数とダッシュに対して とがFloat 値である場合にparseEntryを返すパーサーを使用します。これは、 を使用して非常に簡単にフィルタリングされます。Just DoubleJust 0*NothingcatMaybes

コードは次のとおりです。

module Test where
import Text.Parsec
import qualified Text.Parsec.Token as P
import Text.Parsec.Language (haskellDef)
import Control.Applicative ((<$>))
import Data.Maybe (catMaybes)
lexer = P.makeTokenParser haskellDef

parseFloat = P.float lexer
parseInteger = P.natural lexer 

whiteSpace = P.whiteSpace lexer

parseEntry = try (Just <$> parseFloat)
             <|> try (const (Just 0) <$> (char '*' >> whiteSpace))
             <|> try (const Nothing <$> (char '-' >> whiteSpace))
             <|> (const Nothing <$> parseInteger)


data Line = Line {
      lineNodeNum :: Integer
    , negativeLogProbabilities :: [Double]
    } deriving (Show)

parseLine = do
  nodeNum <- parseInteger
  whiteSpace
  probabilities <- catMaybes <$> many1 parseEntry
  return $ Line { lineNodeNum = nodeNum, negativeLogProbabilities = probabilities }

使用例:

*Test> parseTest parseLine "4  0.123  1.452  0.667  *  3.460  149 - -"
Line {lineNodeNum = 4, negativeLogProbabilities = [0.123,1.452,0.667,0.0,3.46]}

問題になる可能性がある (または問題にならない) 唯一の問題は、*-解析に失敗するのではなく、2 つの異なるトークンとして解析されることです。例えば

*Test> parseTest parseLine "4  0.123  1.452  0.667  *  3.460  149 - -*"
Line {lineNodeNum = 4, negativeLogProbabilities = [0.123,1.452,0.667,0.0,3.46,0.0]}

0.0ログ確率の最後にある余分な点に注意してください。

于 2012-05-25T04:58:31.573 に答える