Haskell Parsec lookahead处理int
我正在开发一个Parsec解析器来处理一种有点复杂的数据文件格式(我无法控制这种格式) 我已经取得了很大的进步,但我目前仍坚持以下几点 我需要能够解析这样一行代码:Haskell Parsec lookahead处理int,haskell,parsec,Haskell,Parsec,我正在开发一个Parsec解析器来处理一种有点复杂的数据文件格式(我无法控制这种格式) 我已经取得了很大的进步,但我目前仍坚持以下几点 我需要能够解析这样一行代码: 4 0.123 1.452 0.667 * 3.460 149 - - p_logProb = liftA mkScore (lp <?> "logProb") where lp = try dub <|> string "*" dub = (
4 0.123 1.452 0.667 * 3.460 149 - -
p_logProb = liftA mkScore (lp <?> "logProb")
where lp = try dub <|> string "*"
dub = (++) <$> ((++) <$> many1 digit <*> string ".") <*> many1 digit
语义上,4
是节点枚举,浮动
和*
是负对数概率(因此,*
表示概率为零的负对数)。149
和减号实际上是垃圾,我可以丢弃它们,但我至少需要确保它们不会破坏解析器
以下是我目前掌握的情况:
这可以处理我提到的“垃圾”。它可能更简单,但它可以自己工作
emAnnotationSet = (,,) <$> p_int <*>
(reqSpaces *> char '-') <*>
(reqSpaces *> char '-')
最后,我尝试将logProb
条目与后面的emAnnotationSet
(以整数开头)分开,如下所示:
hmmMatchEmissions = optSpaces *> (V.fromList <$> sepBy p_logProb reqSpaces)
<* optSpaces <* emAnnotationSet <* eol
<?> "matchEmissions"
列196对应于减号前面的整数后面的空格,因此我很清楚问题在于
p_logProb
解析器正在使用整数。我如何解决这个问题,使p\u logProb
解析器正确地使用了lookahead,从而将该输入留给emAnnotationSet
解析器?我不能完全确定您的问题。但是,要解析基于您的描述给出的行,使用Text.Parsec.Token1
中定义的现有lexer并将它们连接在一起会容易得多
下面的代码将该行解析为一个行数据类型,如果需要,您可以从那里进一步处理它。在解析之前,它没有尝试过滤掉-
和整数,而是使用parseEntry
解析器,如果它是浮点值,则只返回一个Double
,对于*
仅返回0,对于整数和破折号则返回Nothing
。然后使用catMaybes
对其进行非常简单的过滤
代码如下:
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,0.0]}
注意日志概率末尾的额外
0.0
。终止概率的整数不能被误认为概率,因为它不包含小数点。lexeme
combinator将解析器转换为跳过尾随空格的解析器
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
隐马尔可夫模型。。。问题是,“*”不应解析为0.0;它是0的负对数,所以它实际上是无穷大(被视为
negLogZero
)。而且,从列表末尾去掉一个无关的0.0
似乎真的很不幸。。。我还对使用现有的lexer持谨慎态度,因为这种文件格式的浮点实际上不是任意的浮点(例如,它们不能是负数,并且不允许使用e-notation)。实际上,我需要解析一组标记,它们要么是“*”要么是使用十进制表示法的浮点,后跟一个整数(然后是两个减号)。这就是我被卡住的地方。啊,我误解了你关于额外0.0的观点。不,我不担心出现-*
。我仍然担心使用lexer。我不想在允许的双精度和*
s列表中允许任意整数。为了澄清,int和减号总是在行的末尾,紧跟在eol
之前。我想我可以得到我需要的。那些星号实际上不应该变成什么;他们和双打实际上成了得分的例子,但我已经为他们设置了构造函数,所以这应该是一个微不足道的改变。谢谢
*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]}
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]]