【问题标题】:Scala Parser - Message LengthScala Parser - 消息长度
【发布时间】:2011-06-11 14:24:31
【问题描述】:

我正在玩弄 Scala 的 Parser 库。我正在尝试为指定长度后跟该长度消息的格式编写解析器。例如:

x.parseAll(x.message, "5helloworld") // result: "hello", remaining: "world"

我不确定如何使用组合器来做到这一点。我首先想到的是:

def message = length ~ body

但显然身体取决于长度,我不知道该怎么做:p

相反,您可以将消息解析器定义为单个解析器(不是解析器的组合),我认为这是可行的(尽管我没有查看单个解析器是否可以提取多个元素?)。

无论如何,我是一个 scala 菜鸟,我只是觉得这太棒了:)

【问题讨论】:

  • 两个答案都有帮助,谢谢。我仍在学习如何以 scala 方式思考,这就是为什么这个问题值得怀疑 :)

标签: parsing scala parser-combinators


【解决方案1】:

您应该使用 into 或其缩写 >>:

scala> object T extends RegexParsers {
     |   def length: Parser[String] = """\d+""".r
     |   def message: Parser[String] = length >> { length => """\w{%d}""".format(length.toInt).r }
     | }
defined module T

scala> T.parseAll(T.message, "5helloworld")
res0: T.ParseResult[String] =
[1.7] failure: string matching regex `\z' expected but `w' found

5helloworld
      ^

scala> T.parse(T.message, "5helloworld")
res1: T.ParseResult[String] = [1.7] parsed: hello

使用时要注意优先级。例如,如果在上面的函数之后添加“~余数”,Scala 会将其解释为length >> ({ length => ...} ~ remainder) 而不是(length >> { length => ...}) ~ remainder。

【讨论】:

    【解决方案2】:

    这听起来不像是上下文无关的语言,所以你需要使用 flatMap :

    def message = length.flatMap(l => bodyOfLength(n))
    

    其中length是Parser[Int]类型,bodyOfLength(n)会基于repN,比如

    def bodyWithLength(n: Int) : Parser[String] 
      = repN(n, elem("any", _ => true)) ^^ {_.mkString}
    

    【讨论】:

    • 所以这对我来说很有意义,我在上面的例子中得到了它。但是我如何“知道” Parser[Int] 上的 flatMap 使用了“get()”(或者它正在做什么从 Parser[Int] 获取 Int 结果)?这可能是一个措辞不当的问题:)
    • 好的 ... 解析器源代码:lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_7_1_final/src/… -- 在 Parser 上有 flatMap 的定义,它在 ParseResult 上调用 flatMapWithNext ... 令人着迷。
    • 其实签名本身就提供了很多:在 Parser[T] 中,flatMap[T](f: T => Parser[U]) : Parser[U]。解析时,它将返回 U。对于上下文中的内容,this 和 f,解析为 U 的唯一方法是应用由 f 返回的解析器。要获得它,必须调用f,为此,需要T arg。获得T 的唯一方法是申请this。 Daniel 是正确的,使用同义词“into”而不是 flatMap 可能更好。 flatMap 是 scala 中具有此签名的函数的常用名称。 into 在上下文中更清晰,并且恰好在 scaladoc 中正确记录。
    • 再次感谢您的回复。 C# 是我的主要语言,我只是在学习演员时偶然发现了 scala,现在继续陷入学习功能方面的兔子洞(每次我认为我“得到”单子时,我都知道我没有)。我应该尝试一下,但是知道 flatMap 已定义,这是否可以像 for { n <- length; m <- bodyWithLength(n); } yield (m) 一样完成(显然无效的代码,我只是不确定如何在我的脑海中连接它)。
    【解决方案3】:

    我不会为此目的使用 pasrer 组合器。但如果你必须这样做或问题变得更复杂,你可以试试这个:

    def times(x :Long,what:String) : Parser[Any] = x match {
    case 1 => what;
    case x => what~times(x-1,what);
    }
    

    如果你想保留一些东西,不要使用 parseAll,使用 parse。 您可以解析长度,将结果存储在可变字段 x 中(我知道很难看,但在这里很有用)并解析主体 x 次,然后解析字符串,其余部分保留在解析器中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 2011-06-09
      • 1970-01-01
      相关资源
      最近更新 更多