Scala: Свести анализатор (~) из анализатора комбинаторов в List? - PullRequest
11 голосов
/ 07 марта 2012

Я написал парсер из комбинаторной библиотеки. Я хочу универсальную функцию, которая превращает гнездо любого размера в список. Как это сделать?

Вот мой пример парсера, который я использую (у моего настоящего парсера очень длинная цепочка ~, поэтому я хочу избежать моего текущего решения, которое приведено в комментарии ниже).

object CombinatorParser extends RegexParsers {

  lazy val a = "a"
  lazy val b = "b"
  lazy val c = "c"
  lazy val content = a ~ b ~ c // ^^ {case a~b => a::b::c::Nil work but I want something more general that work for any ~ length.
}

object CombinatorTesting {

  def main(args:Array[String]) {
    val testChar = "abc"
    val output = CombinatorParser.parseAll(CombinatorParser.content, testChar)
    println(output) // ((a~b)~c) but I want List(a,b,c)
  }
}

Ответы [ 2 ]

20 голосов
/ 07 марта 2012

Это хорошее (и довольно простое) приложение для общих методов программирования, примером которых является shapeless .

Учитывая ваше определение,

object CombinatorParser extends RegexParsers {
  lazy val a = "a"
  lazy val b = "b"
  lazy val c = "c"
  lazy val content = a ~ b ~ c
}

Мы можем рекурсивно определить класс типа, который сгладит его результаты, следующим образом:

import CombinatorParser._

Сначала мы определяем черту, которая (абстрактно) выравнивает произвольное совпадение M до List[String],

trait Flatten[M] extends (M => List[String]) {
  def apply(m : M) : List[String]
}

Затем мы предоставляем экземпляры классов типов для всех форм M, которые нас интересуют: в данном случае String, A ~ B и ParseResult[T] (где A, B и T - это все типы, для которых есть Flatten экземпляров),

// Flatten instance for String
implicit def flattenString = new Flatten[String] {
  def apply(m : String) = List(m) 
}

// Flatten instance for `A ~ B`. Requires Flatten instances for `A` and `B`. 
implicit def flattenPattern[A, B]
  (implicit flattenA : Flatten[A], flattenB : Flatten[B]) =
    new Flatten[A ~ B] {
      def apply(m : A ~ B) = m match {
        case a ~ b => flattenA(a) ::: flattenB(b)
      } 
}

// Flatten instance for ParseResult[T]. Requires a Flatten instance for T.
implicit def flattenParseResult[T]
  (implicit flattenT : Flatten[T]) = new Flatten[ParseResult[T]] {
    def apply(p : ParseResult[T]) = (p map flattenT) getOrElse Nil 
}

Наконец, мы можем определить вспомогательную функцию для упрощения применения Flatten экземпляров для разбора результатов,

def flatten[P](p : P)(implicit flatten : Flatten[P]) = flatten(p)

А теперь мы готовы,

val testChar = "abc"
val output = parseAll(content, testChar)
println(output)          // ((a~b)~c) but I want List(a, b, c)

val flattenedOutput = flatten(output)
println(flattenedOutput) // List(a, b, c)
6 голосов
/ 15 ноября 2012

Если вы предпочитаете решение без общего программирования ...

  def flatten(res: Any): List[String] = res match {
    case x ~ y => flatten(x) ::: flatten(y)
    case None => Nil
    case Some(x) => flatten(x)
    case x:String => List(x)
  }

  val testChar = "abc"
  val output = CombinatorParser.parseAll(CombinatorParser.content, testChar).getOrElse(None)
  println(flatten(output))
...