web-dev-qa-db-fra.com

`break` et` continue` dans `forEach` dans Kotlin

Kotlin a de très jolies fonctions itératives, comme forEach ou repeat, mais je ne parviens pas à faire en sorte que les opérateurs break et continue (avec les opérateurs locaux). et non local):

repeat(5) {
    break
}

(1..5).forEach {
    continue@forEach
}

Le but est d’imiter les boucles habituelles avec une syntaxe fonctionnelle aussi proche que possible. C'était certainement possible dans certaines versions plus anciennes de Kotlin, mais j'ai du mal à reproduire la syntaxe.

Le problème pourrait être un bug avec les étiquettes (M12), mais je pense que le premier exemple devrait quand même fonctionner.

Il me semble que j'ai lu quelque part une astuce/une annotation spéciale, mais je n’ai trouvé aucune référence à ce sujet. Peut ressembler à ceci:

public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
    for (index in 0..times - 1) {
        body(index)
    }
}
76
voddan

Modifier :
Selon Kotlin anciens documents - le lien est brisé , il devrait être possible d’utiliser des annotations. Cependant, il n'est pas encore implémenté.

Break et continue pour les structures de contrôle personnalisées ne sont pas encore implémentés

Le numéro pour cette fonctionnalité a 3 ans, donc je suppose que cela ne va pas être corrigé.


Réponse originale :
Puisque vous fournissez un (Int) -> Unit, vous ne pouvez pas en sortir, car le compilateur ne sait pas qu’il est utilisé dans une boucle.

Vous avez peu d'options:

Utilisez une boucle for régulière:

for (index in 0..times - 1) {
    // your code here
}

Si la boucle est le dernier code de la méthode
vous pouvez utiliser return pour sortir de la méthode (ou return value _ s'il ne s'agit pas de la méthode unit).

Utiliser une méthode
Créez une méthode de méthode de répétition personnalisée qui renvoie Boolean pour continuer.

public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
    for (index in 0..times - 1) {
        if (!body(index)) break
    }
}
31
Yoav Sternberg

Cela imprimera 1 à 5. Le return@forEach agit comme le mot clé continue en Java, ce qui signifie que, dans ce cas, il exécute toujours toutes les boucles, mais passe à l'itération suivante si la valeur est supérieure à 5.

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    nums.forEach {
       if (it > 5) return@forEach
       println(it)
    }
}

Cela imprimera 1 à 10 mais saute 5.

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    nums.forEach {
       if (it == 5) return@forEach
       println(it)
    }
}

Essayez-les à Kotlin Playground .

68
s-hunter

Vous pouvez utiliser retour de l'expression lambda qui imite un continue ou break en fonction de votre utilisation.

Ceci est couvert dans la question connexe: Comment puis-je faire une "pause" ou "continuer" quand dans une boucle fonctionnelle dans Kotlin?

26
Jayson Minard

Comme le la documentation de Kotlin dit , utiliser return est la voie à suivre. La bonne chose à propos de kotlin est que si vous avez des fonctions imbriquées, vous pouvez utiliser des étiquettes pour écrire explicitement d'où provient votre retour:

Fonction Retour de la fonction

fun foo() {
  listOf(1, 2, 3, 4, 5).forEach {
    if (it == 3) return // non-local return directly to the caller of foo()
    print(it)
  }
  println("this point is unreachable")
}

et Retour local (il n'arrête pas de passer par forEach = continuation)

fun foo() {
  listOf(1, 2, 3, 4, 5).forEach lit@{
    if (it == 3) return@lit // local return to the caller of the lambda, i.e. the forEach loop
    print(it)
  }
  print(" done with explicit label")
}

Consultez la documentation, c'est vraiment bien :)

14
cesards

Une pause peut être réalisée en utilisant:

//Will produce"12 done with nested loop"
//Using "run" and a tag will prevent the loop from running again. Using return@forEach if I>=3 may look simpler, but it will keep running the loop and checking if i>=3 for values >=3 which is a waste of time.
fun foo() {
    run loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop // non-local return from the lambda passed to run
            print(it)
        }
    }
    print(" done with nested loop")
}

Et une poursuite peut être réalisée avec:

//Will produce: "1245 done with implicit label"
fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return@forEach // local return to the caller of the lambda, i.e. the forEach loop
        print(it)
    }
    print(" done with implicit label")
}

Comme tout le monde le recommande ici ... lisez la documentation: P https://kotlinlang.org/docs/reference/returns.html#return-at-labels

13
Raymond Arteaga

continue comportement de type dans forEach

list.forEach { item -> // here forEach give you data item and you can use it 
    if () {
        // your code
        return@forEach // Same as continue
    }

    // your code
}

pour le comportement de type break, vous devez utiliser for until

for (index in 0 until list.size) {
    val item = listOfItems[index] // you can use data item now
    if () {
        // your code
        break
    }

    // your code
}
1
Sumit Jain