2012-03-07 8 views
11

Ho scritto un parser dalla libreria combinatoria. Voglio una funzione generica che trasformi qualsiasi dimensione di nest ~ in una lista. Come fare questo ?Scala: appiattisce il parseresult (~) dal parser dei combinatori in Elenco?

Ecco il mio esempio di parser che uso (il mio parser reale ha una catena molto lunga ~ quindi voglio evitare la mia attuale soluzione che è nel commento qui sotto).

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

non credo che sia possibile. Non puoi dividere le tue catene in pezzi più piccoli? Cosa stai cercando di fare esattamente? Forse se dai un po 'più di contesto qualcuno ha una soluzione migliore per questo. – drexin

risposta

19

Questa è una buona (e abbastanza semplice) applicazione per il tipo di tecniche di programmazione generici esemplificato in shapeless.

Data la tua definizione,

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

Possiamo ricorsivamente definire una classe tipo che appiattirlo di risultati come segue,

import CombinatorParser._ 

In primo luogo definiamo un tratto che (astrattamente) appiattisce delle partite arbitraria M ad un List[String],

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

T gallina forniamo istanze di classe tipo per tutte le forme di M che siamo interessati a: in questo caso, String, A ~ B e ParseResult[T] (dove A, B e T sono tutti i tipi per i quali non sono Flatten casi),

// 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 possiamo definire una funzione convenienza per semplificare l'applicazione Flatten casi per analizzare i risultati,

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

e ora siamo pronti a partire,

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

Se si preferisce una soluzione senza programmazione generica ...

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

Grazie per essere semplice. Funziona e basta. – JulienD