El mejor patrón para simular & # 8220; continuar & # 8221; en cierre groovy

StackOverflow https://stackoverflow.com/questions/205660

  •  03-07-2019
  •  | 
  •  

Pregunta

Parece que Groovy no admite break y continue desde dentro de un cierre. ¿Cuál es la mejor manera de simular esto?

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
         // continue to next line...
    }
}
¿Fue útil?

Solución

Solo puedes admitir continuar limpiamente, no romper. Especialmente con cosas como eachLine y each. La incapacidad de admitir la interrupción tiene que ver con la forma en que se evalúan esos métodos, no se tiene en cuenta que no se haya completado el ciclo que se puede comunicar al método. Aquí es cómo apoyar continuar -

Mejor enfoque (suponiendo que no necesita el valor resultante).

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
        return // returns from the closure
    }
}

Si su muestra realmente es así de simple, esto es bueno para la legibilidad.

revs.eachLine { line -> 
    if (!(line ==~ /-{28}/)) {
        // do what you would normally do
    }
}

otra opción, simula lo que normalmente haría una continuación en un nivel de bytecode.

revs.eachLine { line -> 
    while (true) {
        if (line ==~ /-{28}/) {
            break
        }
        // rest of normal code
        break
    }

}

Una posible forma de admitir la interrupción es a través de excepciones:

try {
    revs.eachLine { line -> 
        if (line ==~ /-{28}/) {
            throw new Exception("Break")
        }
    }
} catch (Exception e) { } // just drop the exception

Es posible que desee utilizar un tipo de excepción personalizado para evitar el enmascaramiento de otras excepciones reales, especialmente si tiene otro proceso en curso en esa clase que podría generar excepciones reales, como NumberFormatExceptions o IOExceptions.

Otros consejos

Los cierres no pueden romperse o continuar porque no son construcciones de bucle / iteración. En su lugar, son herramientas utilizadas para procesar / interpretar / manejar la lógica iterativa. Puede ignorar las iteraciones dadas simplemente regresando del cierre sin procesar como en:

revs.eachLine { line -> 
    if (line ==~ /-{28}/) {
            return
    }

}

El soporte de ruptura no se produce en el nivel de cierre, sino que está implícito en la semántica de la llamada al método aceptado el cierre. En resumen, eso significa que en lugar de llamar a " cada " en algo como una colección que está destinada a procesar la colección completa, debe llamar a encontrar que se procesará hasta que se cumpla una determinada condición. La mayoría de las veces (¿todas?) La necesidad de romper con un cierre lo que realmente desea hacer es encontrar una condición específica durante su iteración que haga que el método de búsqueda coincida no solo con sus necesidades lógicas sino también con su intención. Lamentablemente, algunas de las API carecen de soporte para un método de búsqueda ... Archivo por ejemplo. Es posible que todo el tiempo dedicado a discutir si el lenguaje debería incluir ruptura / continuar podría haber sido bien empleado en agregar el método de búsqueda a estas áreas desatendidas. Algo como firstDirMatching (Closure c) o findLineMatching (Closure c) iría lejos y respondería al 99 +% del " ¿por qué no puedo interrumpir ...? & Quot; Preguntas que aparecen en las listas de correo. Dicho esto, es trivial agregar estos métodos a través de MetaClass o Categorías.

class FileSupport {
   public static String findLineMatching(File f, Closure c) {
      f.withInputStream {
         def r = new BufferedReader(new InputStreamReader(it))
         for(def l = r.readLine(); null!=l; l = r.readLine())
             if(c.call(l)) return l
         return null
      }
   }
}

using(FileSupport) { new File("/home/me/some.txt").findLineMatching { line ==~ /-{28}/ }

Otros hacks que involucran excepciones y otras magias pueden funcionar, pero introducen una sobrecarga adicional en algunas situaciones y complican la legibilidad en otras. La verdadera respuesta es mirar tu código y preguntar si realmente estás iterando o buscando.

Si crea previamente un objeto de excepción estático en Java y luego lanza la excepción (estática) desde dentro de un cierre, el costo en tiempo de ejecución es mínimo. El costo real se incurre en la creación de la excepción, no en lanzarla. Según Martin Odersky (inventor de Scala), muchas JVM pueden optimizar las instrucciones de lanzamiento para saltos simples.

Esto se puede usar para simular una ruptura:

final static BREAK = new Exception();
//...
try {
  ... { throw BREAK; }
} catch (Exception ex) { /* ignored */ }

Use return para continuar y cualquier cierre para break .

Ejemplo

Contenido del archivo:

1
2
----------------------------
3
4
5

Código maravilloso:

new FileReader('myfile.txt').any { line ->
    if (line =~ /-+/)
        return // continue

    println line

    if (line == "3")
        true // break
}

Salida:

1
2
3

En este caso, probablemente deberías pensar en el método find () . Se detiene después de la primera vez que se le pasa el cierre devuelve true.

Con rx-java puede transformar un iterable en un observable.

Luego puedes reemplazar continuar con un filtro y break con takeWhile

Aquí hay un ejemplo:

import rx.Observable

Observable.from(1..100000000000000000)
          .filter { it % 2 != 1} 
          .takeWhile { it<10 } 
          .forEach {println it}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top