何时使用scala三重插入符号(^^^)与双插入符号(^^^)以及into方法(>;)

何时使用scala三重插入符号(^^^)与双插入符号(^^^)以及into方法(>;),scala,parser-combinators,Scala,Parser Combinators,有人能解释一下在设计scala解析器组合器时,如何以及何时使用三个插入符号^^^^^(相对于双插入符号^^^^^)?以及何时/如何使用parser.into()方法(>>)。我将从一个使用Scala的选项的示例开始,它在一些重要方面与parser类似,但更容易推理。假设我们有以下两个值: val fullBox: Option[String] = Some("13") val emptyBox: Option[String] = None 选项是一元的,这意味着(部分)我们可以将函数映射到其内

有人能解释一下在设计scala解析器组合器时,如何以及何时使用三个插入符号^^^^^(相对于双插入符号^^^^^)?以及何时/如何使用parser.into()方法(>>)。

我将从一个使用Scala的
选项的示例开始,它在一些重要方面与
parser
类似,但更容易推理。假设我们有以下两个值:

val fullBox: Option[String] = Some("13")
val emptyBox: Option[String] = None
选项
是一元的,这意味着(部分)我们可以
将函数映射到其内容上:

scala> fullBox.map(_.length)
res0: Option[Int] = Some(2)

scala> emptyBox.map(_.length)
res1: Option[Int] = None
只关心
选项是否已满并不罕见,在这种情况下,我们可以将
map
与忽略其参数的函数一起使用:

scala> fullBox.map(_ => "Has a value!")
res2: Option[String] = Some(Has a value!)

scala> emptyBox.map(_ => "Has a value!")
res3: Option[String] = None
选项
是一元的事实也意味着我们可以应用于
选项[A]
函数,该函数接受
A
并返回
选项[B]
,然后获得
选项[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
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
}
这与您的问题相关,因为
解析器
也是一元的,它有
映射
平面映射
方法,其工作方式与
选项
上的方法非常相似。它还有一系列令人困惑的运算符(包括您提到的运算符),这些运算符只是
map
flatMap
的别名:

(parser ^^ transformation) == parser.map(transformation)
(parser ^^^ replacement) == parser.map(_ => replacement)
(parser >> nextStep) == parser.flatMap(nextStep)
例如,您可以编写以下内容:

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
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
}

每个解析器的行为方式与上面的
选项之一相当。

完美!简短、简洁、务实、切中要害的回答!非常感谢!再次感谢您的简洁回答!尽管我知道Scala的优势之一是它的简洁性,至少与Java相比,但在编写解析器时是否完全有必要使用这些插入符号,或者开发人员是否可以编写“长时间”的映射引用(我在自己的代码中支持这一点,只是为了可读性)?我假设编译器将优化并生成等效代码,对吗?