在设计scala解析器组合器时,有人可以解释如何以及何时使用三重插入符^^^(vs双插入符号^^)?以及何时/如何使用parser.into()方法(>>)。
答案 0 :(得分:16)
我将首先使用Scala的Option
类型的示例,它在Parser
的某些重要方面类似,但可以更容易推理。假设我们有以下两个值:
val fullBox: Option[String] = Some("13")
val emptyBox: Option[String] = None
Option
是monadic,这意味着(部分)我们可以map
对其内容执行一个函数:
scala> fullBox.map(_.length)
res0: Option[Int] = Some(2)
scala> emptyBox.map(_.length)
res1: Option[Int] = None
只关心Option
是否已满,这种情况并不少见,在这种情况下,我们可以使用map
来忽略其参数的函数:
scala> fullBox.map(_ => "Has a value!")
res2: Option[String] = Some(Has a value!)
scala> emptyBox.map(_ => "Has a value!")
res3: Option[String] = None
Option
是monadic这一事实也意味着我们可以向Option[A]
申请一个A
并返回Option[B]
并获得{{1}的函数}}。对于此示例,我将使用一个尝试将字符串解析为整数的函数:
Option[B]
现在我们可以写下以下内容:
def parseIntString(s: String): Option[Int] = try Some(s.toInt) catch {
case _: Throwable => None
}
这与您的问题都相关,因为scala> fullBox.flatMap(parseIntString)
res4: Option[Int] = Some(13)
scala> emptyBox.flatMap(parseIntString)
res5: Option[Int] = None
scala> Some("not an integer").flatMap(parseIntString)
res6: Option[Int] = None
也是一元的,并且Parser
和map
方法的工作方式与flatMap
上的方法非常相似。它还有一堆令人困惑的运算符(I've ranted about before),包括你提到的运算符,这些运算符只是Option
和map
的别名:
flatMap
例如,您可以编写以下内容:
(parser ^^ transformation) == parser.map(transformation)
(parser ^^^ replacement) == parser.map(_ => replacement)
(parser >> nextStep) == parser.flatMap(nextStep)
每个解析器的行为方式与上述object MyParser extends RegexParsers {
def parseIntString(s: String) = try success(s.toInt) catch {
case t: Throwable => err(t.getMessage)
}
val digits: Parser[String] = """\d+""".r
val numberOfDigits: Parser[Int] = digits ^^ (_.length)
val ifDigitsMessage: Parser[String] = digits ^^^ "Has a value!"
val integer: Parser[Int] = digits >> parseIntString
}
示例之一相同。