2012-03-07 11 views
11

Escribí algunos analizadores de la biblioteca combinatoria. Quiero una función genérica que transforme cualquier tamaño de nido ~ en una lista. Como hacer esto ?Scala: ¿Allanar el parseresult (~) del analizador de combinadores en List?

Aquí está mi ejemplo de analizador que uso (mi analizador real tiene una cadena muy larga ~ por lo que quiero evitar mi solución actual que está en comentario a continuación).

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) 
    } 
} 
+0

no creo que eso sea posible. ¿No puedes dividir tus cadenas en pedazos más pequeños? ¿Qué estás tratando de hacer exactamente? Tal vez si le das un poco más de contexto alguien tiene una mejor solución para esto. – drexin

Respuesta

19

Ésta es una buena (y bastante simple) la aplicación de la clase de técnicas de programación genéricas ejemplificado en shapeless.

Dada su definición,

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

Podemos definir de forma recursiva una clase de tipo que aplanarlo de resultados de la siguiente manera,

import CombinatorParser._ 

Primero definimos un rasgo que (en abstracto) se aplana un partido arbitraria M a un List[String],

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

T gallina proporcionamos instancias de la clase de tipo para todas las formas de M que nos interesan: en este caso, String, A ~ B y ParseResult[T] (donde A, B y T son todos los tipos para los que no son Flatten casos),

// 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 
} 

Finalmente podemos definir una función de conveniencia para simplificar la aplicación de Flatten casos para analizar los resultados,

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

y ahora estamos listos para ir,

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

Si prefiere una solución sin necesidad de programación genérica ...

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)) 
+0

Gracias por ser simple. Simplemente funciona. – JulienD

Cuestiones relacionadas