Concat解析结果

时间:2012-03-11 19:42:30

标签: haskell parsec

我可以创建一个解析器,它可以处理两个或三个数字,用逗号分隔,如下所示:

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之外处理它?例如,Scala的解析器组合器可以执行此操作:

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

我想这样做是为了在不同的地方模式匹配解析器。例如,在Scala中,我可以这样做:

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

其中input是要解析的字符串。 parsec是否具有允许类似行为的内置函数?如果不是如何自己建立这样的行为?

1 个答案:

答案 0 :(得分:7)

你可以使用applicative functor来做到这一点。模式通常是:

import Control.Applicative

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

f是一个在这种情况下需要3个参数的函数,而a1a2a3是值的适用函数,可以作为参数传递给{ {1}},例如ff :: Int -> Int -> Int -> Foo可以有a1, 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;这两个表达式是等价的。

PS。你也可以使用applicative functor定义f <$> a1 <*> a2 <*> a3(monad接口更“重量级”,我个人试图避免它):

number