web-dev-qa-db-fra.com

Pouvez-vous rompre une fermeture Groovy "chaque"?

Est-il possible de break à partir d'un Groovy .each{Closure}, ou devrais-je utiliser une boucle classique à la place?

141
tinny

Non, vous ne pouvez pas abandonner un "chacun" sans lever une exception. Vous voulez probablement une boucle classique si vous voulez que la pause soit annulée dans certaines conditions.

Alternativement, vous pouvez utiliser une fermeture "trouver" au lieu d'une fermeture individuelle et renvoyer true lorsque vous auriez fait une pause.

Cet exemple va abandonner avant de traiter toute la liste:

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
}

Impressions

1
2
3
4
5

mais n'imprime pas 6 ou 7.

Il est également très facile d'écrire vos propres méthodes d'itérateur avec un comportement de rupture personnalisé acceptant les fermetures:

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
}

Imprime aussi:

1
2
3
4
5    
186
Ted Naleid

Remplacez la boucle each par la fermeture any.

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

    println element

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

Sortie

1
3
51
Michal Z m u d a

Non, vous ne pouvez pas rompre avec une fermeture à Groovy sans lever une exception. De plus, vous ne devriez pas utiliser d'exceptions pour le flux de contrôle.

Si vous avez envie de sortir d'une fermeture, vous devriez probablement commencer par vous demander pourquoi vous voulez le faire et non pas comment le faire. La première chose à considérer pourrait être la substitution de la fermeture en question à l’une des fonctions de Groovy (conceptuelles) d’ordre supérieur. L'exemple suivant:

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

devient

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

devient

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

ce qui contribue également à la clarté. Cela énonce beaucoup mieux l'intention de votre code.

L'inconvénient potentiel des exemples présentés est que l'itération ne s'arrête que tôt dans le premier exemple. Si vous avez des problèmes de performances, vous voudrez peut-être l'arrêter immédiatement.

Toutefois, dans la plupart des cas d'utilisation impliquant des itérations, vous pouvez généralement recourir à l'une des méthodes de recherche, grep, collecte, injection, etc. de Groovy. Ils prennent généralement quelques "configurations" puis "savent" comment faire l'itération pour vous, afin que vous puissiez réellement éviter les boucles impératives dans la mesure du possible.

10
Kai Sternad

Juste en utilisant une fermeture spéciale

// 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
}
2
sea-kg