正如我所说,它用于参数化解析器,但让我们通过一个示例来说明清楚。
让我们从一个简单的解析器开始,它解析一个数字后跟一个单词:
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)
}
这几乎已经足够了,除了我现在输了n
并且what
:它只返回“列表(香蕉,苹果,橙子)”,而不是应该有多少,以及它们是什么。我可以这样做:
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
?这不是一个单子/理解的东西吗?” 为什么,是的,是的!:-) 这是另一种写作方式listOfThings
:
def listOfThings = for {
nOfWhat <- number ~ word <~ ":"
n ~ what = nOfWhat
list <- things(n.toInt)
} yield new ~(n.toInt, new ~(what, list))
我不这样做n ~ what <- number ~ word <~ ":"
是因为它使用了filter
orwithFilter
在 Scala 中,而Parsers
. 但这里甚至还有另一种写法,它没有完全相同的语义,但产生相同的结果:
def listOfThings = for {
n <- number
what <- word
_ <- ":" : Parser[String]
list <- things(n.toInt)
} yield new ~(n.toInt, new ~(what, list))
这甚至可能让人认为“单子无处不在”的说法可能有一定的意义。:-)