2012-04-04 18 views
8

Digamos que quiero analizar una cadena con varios corchetes de apertura y cierre (utilicé paréntesis en el título porque creo que es más común - la pregunta es la misma sin embargo) entonces que obtengo todos los niveles superiores separados en una lista.Paréntesis coincidentes en Scala --- enfoque funcional

dado:

[hello:=[notting],[hill]][3.4(4.56676|5.67787)][the[hill[is[high]]not]] 

Quiero:

List("[hello:=[notting],[hill]]", "[3.4(4.56676|5.67787)]", "[the[hill[is[high]]not]]") 

La forma en que estoy haciendo esto es contando la apertura y cierre de corchetes y añadiendo a la lista cada vez que tengo a mi contador a 0. Sin embargo, tengo un código de imperativo feo. Puede suponer que la secuencia original está bien formada.

Mi pregunta es: ¿cuál sería un buen enfoque funcional para este problema?

Notas: He pensado en usar la construcción for ... yield pero dado el uso de los contadores no puedo obtener un condicional simple (debo tener condicionales solo para actualizar los contadores también) y no sé cómo Podría usar esta construcción en este caso.

+0

Ver "combinadores analizador": http://stackoverflow.com/search?q = scala + parser + combinators –

+1

Un caso similar: http://blog.tmorris.net/haskell-scala-java-7-functional-java-java/. El código en los comentarios es el bit más útil. –

+0

@AlexanderAzarov, cada vez que juego con los combinadores de analizadores, siento que necesitaré más experiencia para poder obtener una solución en un tiempo casi seguro. ¿Es excesivo aquí? – huynhjl

Respuesta

7

solución rápida biblioteca utilizando Scala analizador combinador:

import util.parsing.combinator.RegexParsers 

object Parser extends RegexParsers { 
    lazy val t = "[^\\[\\]\\(\\)]+".r 

    def paren: Parser[String] = 
    ("(" ~ rep1(t | paren) ~ ")" | 
    "[" ~ rep1(t | paren) ~ "]") ^^ { 
     case o ~ l ~ c => (o :: l ::: c :: Nil) mkString "" 
    } 

    def all = rep(paren) 

    def apply(s: String) = parseAll(all, s) 
} 

Comprobación en REPL:

scala> Parser("[hello:=[notting],[hill]][3.4(4.56676|5.67787)][the[hill[is[high]]not]]") 
res0: Parser.ParseResult[List[String]] = [1.72] parsed: List([hello:=[notting],[hill]], [3.4(4.56676|5.67787)], [the[hill[is[high]]not]]) 
+0

Parece tan fácil cuando lo haces. He pasado como un par de horas en general con esto y lo que tenía era más o menos así: '" ["~ rep (paren ~ opt (t)) ~"] "| "[" ~ rep (t ~ opt (paren)) ~ "]" '. – huynhjl

0

Prueba esto:

val s = "[hello:=[notting],[hill]][3.4(4.56676|5.67787)][the[hill[is[high]]not]]" 
s.split("]\\[").toList 

devuelve:

List[String](
    [hello:=[notting],[hill], 
    3.4(4.56676|5.67787), 
    the[hill[is[high]]not]] 
) 
+0

Vea este ejemplo: "[f [triste] [agregar] dir] [er] [p]". Lo que sugirió claramente no resuelve el caso general. Además, no estoy buscando una solución rápida, tengo un código que funciona perfectamente. – PhyBandit

+0

estaba operando en el caso específico suministrado. La respuesta de @hyunhjl en particular es bastante interesante ... – virtualeyes

4

¿Qué pasa:

def split(input: String): List[String] = { 
    def loop(pos: Int, ends: List[Int], xs: List[String]): List[String] = 
    if (pos >= 0) 
     if ((input charAt pos) == ']') loop(pos-1, pos+1 :: ends, xs) 
     else if ((input charAt pos) == '[') 
     if (ends.size == 1) loop(pos-1, Nil, input.substring(pos, ends.head) :: xs) 
     else loop(pos-1, ends.tail, xs) 
     else loop(pos-1, ends, xs) 
    else xs 
    loop(input.length-1, Nil, Nil) 
} 

scala> val s1 = "[hello:=[notting],[hill]][3.4(4.56676|5.67787)][the[hill[is[high]]not]]" 
s1: String = [hello:=[notting],[hill]][3.4(4.56676|5.67787)][the[hill[is[high]]not]] 

scala> val s2 = "[f[sad][add]dir][er][p]" 
s2: String = [f[sad][add]dir][er][p] 

scala> split(s1) foreach println 
[hello:=[notting],[hill]] 
[3.4(4.56676|5.67787)] 
[the[hill[is[high]]not]] 

scala> split(s2) foreach println 
[f[sad][add]dir] 
[er] 
[p] 
+0

+1, mi respuesta se basó en el caso específico, y no en el genérico, que claramente requiere una solución más compleja y aparentemente iterativa. – virtualeyes

2

Dadas sus necesidades contando el paréntesis parece perfectamente bien. ¿Cómo lo harías de una manera funcional? Puede hacer que el estado pase explícitamente.

Así que primero definimos nuestro estado que se acumula en los resultados blocks o concatena la siguiente block y realiza un seguimiento de la profundidad:

case class Parsed(blocks: Vector[String], block: String, depth: Int) 

Entonces escribimos una pura función que procesa que devuelve el siguiente estado. Afortunadamente, podemos mirar cuidadosamente esta función y asegurarnos de que sea correcta.

def nextChar(parsed: Parsed, c: Char): Parsed = { 
    import parsed._ 
    c match { 
    case '[' | '(' => parsed.copy(block = block + c, 
            depth = depth + 1) 
    case ']' | ')' if depth == 1 
        => parsed.copy(blocks = blocks :+ (block + c), 
            block = "", 
            depth = depth - 1) 
    case ']' | ')' => parsed.copy(block = block + c, 
            depth = depth - 1) 
    case _   => parsed.copy(block = block + c) 
    } 
} 

Entonces sólo utilizó un foldLeft para procesar los datos con un estado inicial:

val data = "[hello:=[notting],[hill]][3.4(4.56676|5.67787)][the[hill[is[high]]not]]" 
val parsed = data.foldLeft(Parsed(Vector(), "", 0))(nextChar) 
parsed.blocks foreach println 

que devuelve:

[hello:=[notting],[hill]] 
[3.4(4.56676|5.67787)] 
[the[hill[is[high]]not]] 
+0

Maldita sea, acabo de tener la misma idea. – Debilski

2

dispondrá de una solución imprescindible feo, así que por qué no hacer uno guapo? :)

Esta es una traducción obligada de la solución de huynhjl, pero simplemente publicando para mostrar que en ocasiones imperativo es conciso y quizás más fácil de seguir.

def parse(s: String) = { 
    var res = Vector[String]() 
    var depth = 0 
    var block = "" 
    for (c <- s) { 
     block += c 
     c match { 
     case '[' => depth += 1 
     case ']' => depth -= 1 
        if (depth == 0) { 
         res :+= block 
         block = "" 
        } 
     case _ => 
     } 
    } 
    res 
    } 
Cuestiones relacionadas