2009-04-19 5 views
28

Me gustaría regresar de un cierre, como lo haría si utilizara una declaración de corte en un bucle.¿Cómo puede uno regresar de un cierre maravilloso y detener su ejecución?

Por ejemplo:

largeListOfElements.each{ element-> 
    if(element == specificElement){ 
     // do some work   
     return // but this will only leave this iteration and start the next 
    } 
} 

En la anterior sentencia if quisiera dejar de iterar a través de la lista y dejar el cierre para evitar repeticiones innecesarias.

He visto una solución en la que se lanza una excepción dentro del cierre y queda atrapada afuera, pero no me gusta esa solución.

¿Hay alguna solución a esto, además de cambiar el código para evitar este tipo de algoritmo?

Respuesta

24

Creo que desea utilizar find en lugar de cada uno (al menos para el ejemplo especificado). Los cierres no son compatibles directamente con el descanso.

Debajo de las cubiertas, groovy en realidad no utiliza un cierre, ya sea para encontrarlo, usa un bucle for.

Alternativamente, podría escribir su propia versión mejorada de find/cada iterador que toma un cierre de prueba condicional, y otro cierre para llamar si se encuentra una coincidencia, rompiéndola si se cumple una coincidencia.

He aquí un ejemplo:

 
Object.metaClass.eachBreak = { ifClosure, workClosure -> 
    for (Iterator iter = delegate.iterator(); iter.hasNext();) { 
     def value = iter.next() 
     if (ifClosure.call(value)) { 
      workClosure.call(value) 
      break 
     }   
    } 
} 

def a = ["foo", "bar", "baz", "qux"] 

a.eachBreak({ it.startsWith("b") }) { 
    println "working on $it" 
} 

// prints "working on bar" 
2

Creo que estás trabajando en el nivel incorrecto de abstracción. El bloque .each hace exactamente lo que dice: ejecuta el cierre una vez para cada elemento. Lo que probablemente desee en su lugar es usar List.indexOf para encontrar el specificElement correcto, y luego hacer el trabajo que necesita para hacer en él.

3

Si desea procesar todos los elementos hasta que se encuentre una específica también se puede hacer algo como esto:

largeListOfElements.find { element -> 
    // do some work 
    element == specificElement 
} 

Aunque se puede usar esto con cualquier tipo de "condición de ruptura". acabo de utilizar este para procesar los primeros n elementos de una colección devolviendo

counter++ >= n 

al final del cierre.

1

Como entiendo groovy, la forma de atajar este tipo de bucles sería arrojar una excepción definida por el usuario.No sé lo que sería la sintaxis (no un programador grrovy), pero se ejecuta en la JVM maravilloso por lo que sería algo algo como:

class ThisOne extends Exception {Object foo; ThisOne(Object foo) {this.foo=foo;}} 

try { x.each{ if(it.isOk()) throw new ThisOne(it); false} } 
catch(ThisOne x) { print x.foo + " is ok"; }  
1

Después de la respuesta de paulmurray Yo mismo no estaba seguro de lo que sucedería con una excepción lanzada desde el interior de un cierre, por lo que nos prepararon rápidamente un caso de prueba JUnit que es fácil pensar:

class TestCaseForThrowingExceptionFromInsideClosure { 

    @Test 
    void testEearlyReturnViaException() { 
     try { 
      [ 'a', 'b', 'c', 'd' ].each {     
       System.out.println(it) 
       if (it == 'c') { 
        throw new Exception("Found c") 
       } 
      } 
     } 
     catch (Exception exe) { 
      System.out.println(exe.message) 
     } 
    } 
} 

la salida del anterior es:

a 
b 
c 
Found c 

Pero recordar que "no se debería utilizar excepciones para el control de flujo", véase en particular la cuestión de desbordamiento de pila: Why not use exceptions as regular flow of control?

Así que la solución anterior es menos que ideal en cualquier caso. Sólo tiene que utilizar:

class TestCaseForThrowingExceptionFromInsideClosure { 

    @Test 
    void testEarlyReturnViaFind() { 
     def curSolution 
     [ 'a', 'b', 'c', 'd' ].find {     
      System.out.println(it) 
      curSolution = it 
      return (it == 'c') // if true is returned, find() stops 
     } 
     System.out.println("Found ${curSolution}") 
    } 
} 

La salida de la anterior es también:

a 
b 
c 
Found c 
Cuestiones relacionadas