【问题标题】:Scala combinator parser, what does >> mean?Scala组合器解析器,>>是什么意思?
【发布时间】:2011-12-20 05:14:53
【问题描述】:

我对scala中的“>>”有点困惑。 Daniel 在Scala parser combinators parsing xml? 中说,它可用于根据前一个解析器的结果对解析器进行参数化。有人可以给我一些例子/提示吗?我已经阅读了scaladoc,但仍然不明白。

谢谢

【问题讨论】:

    标签: parsing scala parser-combinators


    【解决方案1】:

    正如我所说,它用于参数化解析器,但让我们通过一个示例来说明清楚。

    让我们从一个简单的解析器开始,它解析一个数字后跟一个单词:

    def numberAndWord = number ~ word
    def number        = "\\d+".r
    def word          = "\\w+".r
    

    RegexParsers 下,这将解析诸如“3 个水果”之类的内容。

    现在,假设您还想要这些“n 事物”的列表。例如,“3 种水果:香蕉、苹果、橙子”。让我们尝试解析它,看看它是如何进行的。

    首先,我如何解析“N”个东西?碰巧,有一个repN 方法:

    def threeThings = repN(3, word)
    

    这将解析“香蕉苹果橙”,而不是“香蕉,苹果,橙”。我需要一个分隔符。 repsep 提供了这一点,但这不会让我指定我想要多少次重复。所以,让我们自己提供分隔符:

    def threeThings = word ~ repN(2, "," ~> word)
    

    好的,那句话。我们现在可以编写整个示例,包括三件事,如下所示:

    def listOfThings = "3" ~ word ~ ":" ~ threeThings
    def word         = "\\w+".r
    def threeThings  = word ~ repN(2, "," ~> word)
    

    这种工作,除了我在 3 中修复“N”。我想让用户指定多少。这就是>>,也称为into(是的,它是flatMap for Parser)的用武之地。首先我们改一下threeThings

    def things(n: Int) = n match {
      case 1          => word ^^ (List(_))
      case x if x > 1 => word ~ repN(x - 1, "," ~> word) ^^ { case w ~ l => w :: l }
      case x          => err("Invalid repetitions: "+x)
    }
    

    这比您预期的要复杂一些,因为我强制它返回Parser[List[String]]。但是如何将参数传递给事物?我的意思是,这行不通:

    def listOfThings = number ~ word ~ ":" ~ things(/* what do I put here?*/)
    

    但我们可以这样重写:

    def listOfThings = (number ~ word <~ ":") >> {
      case n ~ what => things(n.toInt)
    }
    

    这几乎已经足够了,除了我现在丢失了nwhat:它只返回“List(banana, apple, orange)”,而不是应该有多少,以及它们是什么。我可以这样做:

    def listOfThings   = (number ~ word <~ ":") >> {
      case n ~ what => things(n.toInt) ^^ { list => new ~(n.toInt, new ~(what, list)) }
    }
    def number         = "\\d+".r
    def word           = "\\w+".r
    def things(n: Int) = n match {
      case 1          => word ^^ (List(_))
      case x if x > 1 => word ~ repN(x - 1, "," ~> word) ^^ { case w ~ l => w :: l }
      case x          => err("Invalid repetitions: "+x)
    }
    

    只是最后的评论。您可能想知道问自己flatMap 是什么意思?这不是 monad/for-comprehension 的东西吗?” 为什么,是的,是的! :-) 这是listOfThings的另一种写法:

    def listOfThings   = for {
      nOfWhat  <- number ~ word <~ ":"
      n ~ what = nOfWhat
      list     <- things(n.toInt)
    }  yield new ~(n.toInt, new ~(what, list))
    

    我没有做n ~ what &lt;- number ~ word &lt;~ ":",因为它在Scala 中使用filterwithFilterParsers 没有实现。但这里甚至还有另一种写法,它没有完全相同的语义,但产生相同的结果:

    def listOfThings   = for {
      n    <- number
      what <- word
      _    <- ":" : Parser[String]
      list <- things(n.toInt)
    }  yield new ~(n.toInt, new ~(what, list))
    

    这甚至可能让人认为“monads are无处不在”的说法可能有一些东西。 :-)

    【讨论】:

    • 谢谢,但还有一件事。我仍然不了解 { list => new ~(n.toInt, new ~(what, list)) } } 中的 new。我对 scala 还比较陌生,所以我只知道使用 new 从类中生成实例。
    • @Tg。这正是这里正在发生的事情。当您使用~ 运算符解析某些内容时,结果将是~ class 的一个实例。我本可以——可以说,我应该——改为返回一个元组——它更容易。但是,我想在示例代码中保留原始返回值。
    • @Tg。不对。 Scala 的函数是对象。然而,这里~ 类和Parser~ 运算符在设计上具有相同的名称,并且运算符~ 返回一个Parser[~[T,U]],其中TU 是此运算符适用的解析器的类型。但是,类和运算符可以有完全不同的名称。顺便说一句,这个对偶运算符/类也可以通过:: 找到。
    • 感谢您的明确解释。看来我还有很多东西要学。但是,是的,我在之前的评论中真正的意思是“我被误认为是普通操作员”,所以第一次让我感到困惑。
    【解决方案2】:

    &gt;&gt; 方法采用一个函数,该函数得到解析器的结果,并使用它来构造一个新的解析器。如前所述,这可用于根据前一个解析器的结果参数化解析器。

    示例

    以下解析器解析带有n + 1 整数值的行。第一个值n 表示要遵循的值的数量。这个第一个整数被解析,然后这个解析的结果被用来构造一个解析器来解析n更多的整数。

    解析器定义

    以下行假设您可以使用parseInt: Parser[Int] 解析整数。它首先解析一个整数值n,然后使用&gt;&gt; 解析n 其他整数,这些整数构成了解析器的结果。所以解析器不会返回初始的n(尽管它是返回列表的大小)。

    def intLine: Parser[Seq[Int]] = parseInt >> (n => repN(n,parseInt))
    

    有效输入

    1 42
    3 1 2 3
    0
    

    无效输入

    0 1
    1
    3 42 42
    

    【讨论】:

    • 这不是 flatMap 的别名吗?
    • @paradigmatic 根据 scaladocs,它是 into 的别名。根据消息来源,&gt;&gt; 调用 into,后者调用 flatMap
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-17
    • 1970-01-01
    • 2023-03-27
    • 2014-10-16
    • 2017-01-07
    • 1970-01-01
    • 2016-06-10
    相关资源
    最近更新 更多