Я не знаю, как захватить подгруппы регулярных выражений в Scala.
Скажем, зелья либо простые, либо сделаны из других зелий, и они называются <adjective> <noun>
. Существуют следующие формулы рецептов:
val recipe = "white phial potion is 3 fortify health potions, 1 vigorous stamina potion, 5 plentiful magicka potions."
Я хочу проанализировать формулы в следующем домене:
case class Potion(name: String)
case class Formula(potion: Potion, ingredients: Option[List[(Potion, Int)]])
Пока я придумал шаблон
val recipePattern: Regex = """(\w+ \w+) potion is( (\d+) (\w+ \w+) (?:potion|potions)[,.])+""".r
но я понятия не имею, как продолжить и захватить итерационные подгруппы.
Невозможно захватить повторяющиеся группы с помощью регулярных выражений. Последнее совпадение определенной группы перезапишет все предыдущие совпадения той же группы.
Я рекомендую использовать подходящую библиотеку синтаксического анализа для анализа таких строк. Например, используя fastparse:
import scala.util.{Try, Success, Failure}
case class Potion(name: String)
object Potion {
def apply(adjective: String, noun: String): Potion = Potion(s"$adjective $noun")
}
case class Formula(potion: Potion, ingredients: List[(Potion, Int)])
object Formula {
import fastparse._, SingleLineWhitespace._
object Parser {
def number[_: P] = P(CharsWhileIn("0-9").!).map(_.toInt)
def word[_: P] = P(CharsWhileIn("a-z").!)
def ingredient[_: P] = P(number ~ word ~ word ~ "potion"~~"s".?).map {
case (count, adj, noun) => (Potion(adj, noun), count)
}
def formula[_: P] = P(word ~ word ~ "potion" ~ "is" ~ ingredient.rep(sep = ",") ~ "." ~ End).map {
case (adj, noun, ingredients) => Formula(Potion(adj, noun), ingredients.toList)
}
}
def parse(formula: String): Try[Formula] = {
fastparse.parse(formula, Parser.formula(_)) match {
case Parsed.Success(result, _) => Success(result)
case failure: Parsed.Failure => Failure(new RuntimeException(failure.trace().longMsg))
}
}
}
Результат:
scala> Formula.parse("white phial potion is 3 fortify health potions, 1 vigorous stamina potion, 5 plentiful magicka potions.")
res0: scala.util.Try[core.Test.Formula] = Success(Formula(Potion(white phial),List((Potion(fortify health),3), (Potion(vigorous stamina),1), (Potion(plentiful magicka),5))))
Это невозможно сделать, потому что значение повторяющейся группы захвата перезаписывается при каждой итерации.