あなたが記述しているもの (および and を使用して実装で多かれ少なかれ再発明したものfoldLeft
)~
は、基本的にはモナドの Haskell のsequence
ものです (実際にはアプリケーション ファンクターのみが必要ですが、それはここでは関係ありません)。sequence
単項値のリストを取り、値の単項リストを返します。Parser
はモナドなので、sequence
forは aを a にParser
変更します。List[Parser[A]]
Parser[List[A]]
Scalazは を提供しますが、私の頭の中で、 に必要なインスタンスsequence
を取得する良い方法があるかどうかはわかりません。幸いなことに、非常に簡単に独自のロールを作成できます ( Haskell の定義を直接翻訳しています)。Applicative
Parser
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
次のようにinto
書くことができmap
ます^^
:
val line = parsers.foldRight(success(Nil: List[List[String]])) {
(current, acc) => current into (x => acc ^^ (x :: _))
}
これは、反転ではなく右折を使用していることと、~
s を構築および分解していないことを除いて、あなたの定式化からそれほど離れていません。
効率性について: どちらの実装も不快なコール スタックが発生します。私の経験では、これは Scala のパーサー コンビネータを使用した場合の現実です。たとえば、別の Stack Overflow answerを引用するには:
Scala のパーサー コンビネーターはあまり効率的ではありません。彼らはそうなるように設計されていませんでした。比較的小さな入力で小さなタスクを実行するのに適しています。
私の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 整数グループの行でオーバーフローしません。