您所描述的(以及您在实现中或多或少用foldLeft
and重新发明的~
内容)本质上是 Haskell 的sequence
单子(实际上您只需要一个应用函子,但这在这里无关紧要)。sequence
接受一元值列表并返回一元值列表。Parser
是一个单子,所以sequence
forParser
会将 a 更改List[Parser[A]]
为 a Parser[List[A]]
。
Scalaz给了你sequence
,但我不知道是否有一个很好的方法来获取必要的Applicative
实例Parser
。幸运的是,你可以很容易地自己动手(我直接翻译Haskell 定义):
import scala.util.parsing.combinator._
object parser extends RegexParsers {
val integer = """\d+""".r
val counts = List(1, 2, 3)
val parsers = counts.map(repN(_, integer))
val line = parsers.foldRight(success(Nil: List[List[String]])) {
(m, n) => for { x <- m ; xs <- n } yield (x :: xs)
}
def apply(s: String) = parseAll(line, s)
}
这为我们List(List(1), List(2, 3), List(4, 5, 6))
提供parser("1 2 3 4 5 6")
了所需的 。
(请注意,我在RegexParsers
这里使用的是一个方便的完整示例,但该方法更普遍。)
如果我们对for
理解进行去糖处理,发生的事情可能会更清楚一些:
val line = parsers.foldRight(success(Nil: List[List[String]])) {
(current, acc) => current.flatMap(x => acc.map(x :: _))
}
我们可以写flatMap
asinto
和map
as ^^
:
val line = parsers.foldRight(success(Nil: List[List[String]])) {
(current, acc) => current into (x => acc ^^ (x :: _))
}
这与您的公式相差不远,除了我们使用正确的折叠而不是反转并且没有建立和分解~
s。
About efficiency: Both of our implementations are going to result in unpleasant call stacks. In my experience this is just a fact of life with Scala's parser combinators. To quote another Stack Overflow answer, for example:
Scala's parser combinators aren't very efficient. They weren't
designed to be. They're good for doing small tasks with relatively
small inputs.
我的sequence
-y 方法解决了您问题的“更具可读性”部分,并且几乎可以肯定是使用 Scala 的解析器组合器解决问题的最干净的方法。它比您的实施效率略高,对于几千组左右应该没问题。如果你需要处理更多的事情,你将不得不在scala.util.parsing.combinator
. 我会推荐如下内容:
def parse(counts: Seq[Int], input: String): Option[Seq[Seq[Int]]] = {
val parsed = try {
Some(input.split(" ").map(_.toInt))
} catch {
case _ : java.lang.NumberFormatException => None
}
parsed.flatMap { ints =>
if (ints.length != counts.sum) None
else Some(counts.foldLeft((Seq.empty[Seq[Int]], ints)) {
case ((collected, remaining), count) => {
val (m, n) = remaining.splitAt(count)
(m.toSeq +: collected, n)
}
}._1.reverse)
}
}
没有保证,但在我的系统上,它不会在具有 100k 个整数组的行上溢出。