2012-03-11 75 views
4

我可以创造一个分析器,它可以处理两个或三个数字,用逗号分隔,就像这样:的毗连解析结果

number :: Parser Int 
number = many1 digit >>= return . read <?> "number" 

numbers = do 
    n1 <- number 
    n2 <- char ':' >> number 
    n3 <- optionMaybe $ char ':' >> number 
    return ... -- return all of n1, n2, n3 

只有数是重要的,剩下的就被丢弃。有没有一种方法来连接中间解析结果(n1,n2,n3)来处理它的input之外?例如斯卡拉的解析器组合子可以做到这一点:

def numbers: Parser[Int ~ Int ~ Option[Int]] = // only the important numbers are returned 
    number ~ (":" ~> number) ~ opt(":" ~> number) 

我想,为了在不同的地方做这个模式匹配解析器。例如,在斯卡拉我可以做这样的事情:

val result = input.parseAs(numbers) { 
    case n1 ~ n2 ~ None => // work with n1,n2 
    case n1 ~ n2 ~ Some(n3) => // work with n1,n2,n3 
} 

其中输入是一个字符串解析。 parsec是否有内置函数来允许类似的行为?如果不是我自己如何建立这样的行为?

回答

6

您可以通过使用应用性函子做。所述图案通常是:

import Control.Applicative 

f <$> a1 <*> a2 <*> a3 

f是一些函数,它在这种情况下3个参数,和a1a2a3超过值应用性函子可以作为参数被传递给f,例如,如果f :: Int -> Int -> Int -> Fooa1, a2, a3可能有Parser Int。函子a1, a2, a3将被顺序地施加,其结果将被收集并映射在功能f

在你的情况,你会想做的事:

numbers :: Parser (Int, Int, Maybe Int) 
numbers = 
    (,,) 
    <$> number 
    <*> (char ':' *> number) 
    <*> optionMaybe (char ':' *> number) 

(,,)是一个3元组的构造,所以这是一个函数取3个参数,并返回一个3元组。经过3个解析器与<$>..<*>..图案抬起3元组构造成这里所使用的仿函数,这是在这种情况下Parser的应用,所以整个表达式返回映射函数包裹在仿函数,作为Parser (Int, Int, Maybe Int)的结果。

您还可以使用liftA3 f a1 a2 a3代替f <$> a1 <*> a2 <*> a3;这两个表达式是等价的。

PS。您可以定义number使用应用性函子,太(单子界面更“重量级”,我个人尽量避免吧):

number :: Parser Int 
number = read <$> many1 digit <?> "number"