2008-10-15 10 views

Respuesta

70

Solo se puede admitir continuar limpiamente, no romper. Especialmente con cosas como EachLine y cada uno. La incapacidad para soportar la interrupción tiene que ver con la forma en que se evalúan esos métodos, no se tiene en cuenta la finalización del ciclo que se puede comunicar al método. Aquí se explica cómo admitir continue:

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

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

Si su muestra 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 manera de apoyar ruptura 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 personalizada para evitar enmascarar otras excepciones reales, especialmente si usted tiene otro tratamiento que se realice en ese clase que podría arrojar excepciones reales, como NumberFormatExceptions o IOExceptions.

+30

El uso de excepciones para controlar el flujo del programa es una mala idea. Crear excepciones requiere tomar una instantánea de la pila de llamadas, y eso es costoso. –

+6

No si anula el método que genera la pila de llamadas en la excepción que arroja para no hacer nada. Esa es la ventaja de una excepción personalizada. – shemnon

+9

Eso también es un paso adicional en un esfuerzo por solucionar un problema que no tendrías si usaras un cierre como un cierre en lugar de considerarlo una construcción de lazo. El ejemplo anterior se beneficiaría más si se fijara la intención general poco clara de la lógica para filtrar líneas o encontrar una línea. – Cliff

15

Los cierres no se pueden interrumpir 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 pasar por alto iteraciones dada por la simple devolución del cierre sin transformar como en:

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

} 

apoyo rotura 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 uno" sobre algo así como una colección que pretende procesar toda la colección, debe llamar a find para que procese hasta que se cumpla una determinada condición. La mayoría de las veces (¿todas?) En las que siente la necesidad de abandonar un cierre lo que realmente quiere hacer es encontrar una condición específica durante la iteración, lo que hace que el método de búsqueda no solo coincida 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 pasado argumentando si el lenguaje debería incluir break/continue podría haber sido bien empleado agregando el método de búsqueda a estas áreas descuidadas. Algo así como firstDirMatching (Closure c) o findLineMatching (Closure c) sería muy útil y respondería al 99 +% de "¿por qué no puedo separarme de ...?" preguntas que aparecen en las listas de correo. Dicho esto, es trivial agregar estos métodos usted mismo 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 cortes que implican excepciones y otra magia puede funcionar, pero introducir una sobrecarga adicional en algunas situaciones y enrollar la legibilidad en otros. La verdadera respuesta es mirar su código y preguntar si realmente está iterando o buscando en su lugar.

10

Si pre-crea un objeto Exception estático en Java y luego lanza la excepción (estática) desde el interior de un cierre, el costo de la ejecución es mínimo. El costo real se genera al crear la excepción, no al tirarla. Según Martin Odersky (inventor de Scala), muchas JVM en realidad pueden optimizar las instrucciones de lanzamiento para saltos individuales.

Esto puede ser usado para simular un descanso:

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

Eso ... esa es una idea realmente ingeniosa. Combine esto con enumeraciones para crear una excepción creada previamente para cada enumeración. Diablos: pon un método en la enumeración que lo arroja. Condition.BAD_WEATHER.fire(); – paulmurray

+1

En realidad, la idea no era mía. Martin Odersky (el inventor de Scala) lo demostró como una solución al mismo problema en Scala en una charla que él [dio] (http://www.youtube.com/watch?v=zqFryHC018k&hd=1). Hace muchos años, un profesor de un curso de estructuras de datos en Rensselaer me mostró una solución a un problema que no utilizaba declaraciones 'goto' y luego la misma solución con' goto's. Este último era ** mucho ** más corto. Su punto era que a veces la practicidad gana la elegancia. – Ralph

3

En este caso, probablemente debería pensar en el método find(). Se detiene después de la primera vez que el cierre pasó a ser verdadero.

7

Uso retorno-siguen y cualquier cierre-ruptura.

Ejemplo

contenido del archivo:

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

código Groovy:

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

    println line 

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

de salida:

1 
2 
3 
+0

Eres genial. Tenga cuidado con .any. Si la última declaración en el cierre está asociada con 'verdadero', se detendrá. Si quiere su lógica o quiere que su archivo se procese por completo, coloque 'falso' como la última declaración dentro de cualquier cierre. Me tomó un par de días hasta que vi esta publicación. – user2618844

+1

Si reemplaza el último bloque con solo 'línea ==" 3 "' Creo que es un poco más claro –

+0

¿Por qué esta no es la respuesta más votada? – psaxton

1

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

A continuación, puede reemplazar continuar con un filtro y ruptura con takeWhile

He aquí un ejemplo:

import rx.Observable 

Observable.from(1..100000000000000000) 
      .filter { it % 2 != 1} 
      .takeWhile { it<10 } 
      .forEach {println it} 
Cuestiones relacionadas