¿Se puede romper con un cierre maravilloso "cada"

143

¿Es posible breakdesde un Groovy .each{Closure}, o debería usar un bucle clásico en su lugar?

cascado
fuente

Respuestas:

194

No, no puedes abortar un "cada" sin lanzar una excepción. Es probable que desee un bucle clásico si desea que el descanso se cancele bajo una condición particular.

Alternativamente, podría usar un cierre "buscar" en lugar de un cada y devolver verdadero cuando hubiera hecho un descanso.

Este ejemplo abortará antes de procesar toda la lista:

def a = [1, 2, 3, 4, 5, 6, 7]

a.find { 
    if (it > 5) return true // break
    println it  // do the stuff that you wanted to before break
    return false // keep looping
}

Huellas dactilares

1
2
3
4
5

pero no imprime 6 o 7.

También es realmente fácil escribir sus propios métodos de iterador con un comportamiento de interrupción personalizado que acepte cierres:

List.metaClass.eachUntilGreaterThanFive = { closure ->
    for ( value in delegate ) {
        if ( value  > 5 ) break
        closure(value)
    }
}

def a = [1, 2, 3, 4, 5, 6, 7]

a.eachUntilGreaterThanFive {
    println it
}

También imprime:

1
2
3
4
5    
Ted Naleid
fuente
3
También he enviado un parche a Groovy que agrega un método findResult que realiza un hallazgo en cortocircuito que devuelve el primer resultado no nulo del cierre. Este método podría usarse para cubrir casi todas las situaciones que alguien podría desear salir de cada una de ellas. Verifique el parche para ver si se acepta y se enrolla en groovy (espero 1.8): jira.codehaus.org/browse/GROOVY-4253
Ted Naleid
2
He intentado este caso: def a = [1, 2, 3, 4, 5, 6, 7, -1, -2] Devuelve 1 2 3 4 5 -1 -2. Entonces, "breaK" no funciona.
Phat H. VU
El hallazgo correcto es la opción correcta, cada uno nunca se romperá por retorno
Pushkar
es findmejor que any- vea la otra respuesta a continuación de @Michal que me funciona
Ruibarbo
El parche de Ted Naleid para groovy es muy útil. Utilice findResult en su lugar si realmente necesita el resultado de la operación de búsqueda y no el elemento en sí. Ej: ​def test = [2] test.findResult{ it * 2 }​devolverá 4 en lugar de 2
Doug
57

Reemplace cada lazo con cualquier cierre.

def list = [1, 2, 3, 4, 5]
list.any { element ->
    if (element == 2)
        return // continue

    println element

    if (element == 3)
        return true // break
}

Salida

1
3
Michal Z muda
fuente
Solo un truco. ¿Qué sucede si hay una declaración después de "break"? Esta declaración aún se ejecutará después de la reunión "break".
Phat H. VU
2
@ Phat H. VU He agregado retorno. La declaración después del "descanso" no se ejecutará
Michal Z muda
1
Gracias por su respuesta. Tienes razón. También voto tu respuesta. Más: el método any se define en DefaultGroovyMethods y es una función de predicado que devuelve verdadero si un elemento de una colección satisface el cierre del predicado proporcionado.
Phat H. VU
Usarlo any()de esta manera es un poco engañoso, pero ciertamente funciona y le brinda la capacidad de interrumpir o continuar .
vegemite4me
1
como @ vegemite4me, creo que este es un "truco" pero no entiendes bien el significado de ninguno. Para el mantenimiento, no debe usar esta solución.
MatRt
11

No, no puedes romper un cierre en Groovy sin lanzar una excepción. Además, no debe usar excepciones para el flujo de control.

Si te encuentras con ganas de salir de un cierre, probablemente primero deberías pensar por qué quieres hacer esto y no cómo hacerlo. Lo primero a considerar podría ser la sustitución del cierre en cuestión con una de las funciones de orden superior (conceptual) de Groovy. El siguiente ejemplo:

for ( i in 1..10) { if (i < 5) println i; else return}

se convierte

(1..10).each{if (it < 5) println it}

se convierte

(1..10).findAll{it < 5}.each{println it} 

lo que también ayuda a la claridad. Establece la intención de su código mucho mejor.

El inconveniente potencial en los ejemplos mostrados es que la iteración solo se detiene temprano en el primer ejemplo. Si tiene consideraciones de rendimiento, es posible que desee detenerlo en ese momento.

Sin embargo, para la mayoría de los casos de uso que involucran iteraciones, generalmente puede recurrir a uno de los métodos de búsqueda, grep, recolección, inyección, etc. de Groovy. Por lo general, toman algo de "configuración" y luego "saben" cómo hacer la iteración por usted, para que pueda evitar el bucle imperativo siempre que sea posible.

Kai Sternad
fuente
1
"No, no puedes romper un cierre en Groovy sin lanzar una excepción". Esto es lo que Scala hace, ver dev.bizo.com/2010/01/scala-supports-non-local-returns.html
OlliP
2

Solo usando un cierre especial

// declare and implement:
def eachWithBreak = { list, Closure c ->
  boolean bBreak = false
  list.each() { it ->
     if (bBreak) return
     bBreak = c(it)
  }
}

def list = [1,2,3,4,5,6]
eachWithBreak list, { it ->
  if (it > 3) return true // break 'eachWithBreak'
  println it
  return false // next it
}
kg de mar
fuente
3
y si tiene mil millones de filas y el cierre interno devuelve verdadero en la primera llamada, iterará más de mil millones menos uno de los valores. :(
sbglasius
-4

(1..10) .cada {

si (es <5)

imprimirlo

más

falso retorno

Sagar Mal Shankhala
fuente
2
Esto no se rompe each, simplemente no imprime valores mayores que 4. elseEs superfluo, su código haría lo mismo sin él. Además, puedes probar eachque no se rompe return falsesi pones println "not breaking"justo después elsey justo antes return false.
Stefan van den Akker
Haga al menos un mínimo esfuerzo para formatear su código para facilitar la lectura. Hay una vista previa visual cuando responde y muchas instrucciones sobre cómo hacerlo
Mateusz fue el
-11

Podrías pasar por aquí RETURN. Por ejemplo

  def a = [1, 2, 3, 4, 5, 6, 7]
  def ret = 0
  a.each {def n ->
    if (n > 5) {
      ret = n
      return ret
    }
  }

¡Esto funciona para mi!

Tseveen D
fuente
66
Que es exactamente lo que preguntó el OP, aunque obviamente no es lo que quiso decir.
Szocske
¿Puedo tener una descripción de por qué esto tiene votos negativos? Esto me parece el mismo concepto que lo que dice la respuesta principal (con menos explicación) con +133 votos.
Skepi
1
@Skepi No se puede "romper" por encima del cierre. Puedes romperany método de la matriz volviendo false. No puede romper el eachmétodo de la misma manera.
Nux