Píšem gramatiku kombinétorov Scala syntaktického analyzátora, ktorá číta nové zoznamy slov s oddelenými riadkami, kde sú zoznamy oddelené jednou alebo viacerými prázdnymi riadkami. Vzhľadom na nasledujúci reťazec:
cat
mouse
horse
apple
orange
pear
Chcel by som, aby sa vrátil List(List(cat, mouse, horse), List(apple, orange, pear))
.
Napísal som túto základnú gramatiku, ktorá zaobchádza s slovnými zoznammi ako slovami oddelenými od novej linky. Všimnite si, že som musel prepísať predvolenú definíciu whitespace
.
import util.parsing.combinator.RegexParsers
object WordList extends RegexParsers {
private val eol = sys.props("line.separator")
override val whiteSpace = """[ t]+""".r
val list: Parser[List[String]] = repsep( """w+""".r, eol)
val lists: Parser[List[List[String]]] = repsep(list, eol)
def main(args: Array[String]) {
val s =
"""cat
|mouse
|horse
|
|apple
|orange
|pear""".stripMargin
println(parseAll(lists, s))
}
}
Toto nesprávne považuje prázdne riadky za prázdne zoznamy slov, to znamená, že sa vracia
[8.1] parsed: List(List(cat, mouse, horse), List(), List(apple, orange, pear))
(Poznamenajte si prázdny zoznam v strede.)
Môžem na konci každého zoznamu zadať voliteľný koniec riadku.
val list: Parser[List[String]] = repsep( """w+""".r, eol) <~ opt(eol)
Toto rieši prípad, keď medzi zoznamami existuje jeden prázdny riadok, ale má rovnaký problém s viacerými prázdnymi riadkami.
Pokúsil som sa zmeniť lists
definícia, aby sa umožnili viaceré oddeľovače koncového radu:
val lists:Parser[List[List[String]]] = repsep(list, rep(eol))
ale to visí na vyššie uvedenom vstupe.
Aká je správna gramatika, ktorá bude spracovávať viac prázdnych riadkov ako oddeľovačov?
odpovede:
13 pre odpoveď č. 1Mali by ste sa pokúsiť nastaviť skipWhitespace
na false
namiesto definovania definície medzery. Problém, ktorý máte s prázdnym zoznamom, je spôsobený tým, že repsep
nepoužíva prerušenie riadku na konci zoznamu.Napríklad by ste mali analyzovať prerušenie riadku (alebo prípadne koniec vstupu) po každej položke:
import util.parsing.combinator.RegexParsers
object WordList extends RegexParsers {
private val eoi = """z""".r // end of input
private val eol = sys.props("line.separator")
private val separator = eoi | eol
private val word = """w+""".r
override val skipWhitespace = false
val list: Parser[List[String]] = rep(word <~ separator)
val lists: Parser[List[List[String]]] = repsep(list, rep1(eol))
def main(args: Array[String]) {
val s =
"""cat
|mouse
|horse
|
|apple
|orange
|pear""".stripMargin
println(parseAll(lists, s))
}
}
Znova sú tu aj synchrónne kombinátory. Môžete získať prakticky to isté (ale s Arrayami namiesto zoznamov) s niečím oveľa jednoduchším:
s.split("n{2,}").map(_.split("n"))