Как я уже сказал, он служит для параметризации парсера, но давайте разберем пример, чтобы прояснить ситуацию.
Давайте начнем с простого парсера, который разбирает число, за которым следует слово:
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)
Это работает, за исключением того, что я исправляю "N" в 3. Я хочу позволить пользователю указать, сколько. И вот тут появляется >>
, также известный как into
(и, да, это flatMap
для 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
или withFilter
в Scala, который не реализован Parsers
. Но вот еще один способ написать это, который не имеет точно такой же семантики, но дает те же результаты:
def listOfThings = for {
n <- number
what <- word
_ <- ":" : Parser[String]
list <- things(n.toInt)
} yield new ~(n.toInt, new ~(what, list))
Это может даже привести к мысли, что, возможно, утверждение, что "монады повсюду", может иметь к этому какое-то отношение : -)