Dans Kotlin, je ne peux pas faire de break
ou continue
dans une boucle de fonction et mon lambda - comme je peux le faire à partir d'une boucle normale de for
. Par exemple, cela ne fonctionne pas:
(1..5).forEach {
continue@forEach // not allowed, nor break@forEach
}
Il y a ancienne documentation qui mentionne que cela est disponible mais il semble qu'il n'ait jamais été implémenté. Quelle est la meilleure façon d'obtenir le même comportement lorsque je veux continue
ou break
à partir du lambda?
Remarque: cette question est intentionnellement écrite et répondue par l'auteur ( questions en réponse ), de sorte que les réponses idiomatiques aux sujets Kotlin fréquemment posés sont présentes dans SO. Aussi pour clarifier certaines réponses vraiment anciennes écrites pour les alphas de Kotlin qui ne sont pas exactes pour le Kotlin actuel.
Il existe d'autres options que celles que vous demandez qui offrent des fonctionnalités similaires. Par exemple:
Vous pouvez éviter de traiter certaines valeurs en utilisant filter
: ( comme un continue
)
dataSet.filter { it % 2 == 0 }.forEach {
// do work on even numbers
}
Vous pouvez arrêter une boucle fonctionnelle en utilisant takeWhile
: ( comme un break
)
dataSet.takeWhile { it < 10 }.forEach {
// do work on numbers as long as they are < 10, otherwise stop
}
Un exemple plus complexe, bien que absurde, où vous souhaitez effectuer un traitement, ignorer certaines valeurs résultantes, puis vous arrêter à un ensemble de conditions différentes, serait:
dataSet.asSequence()
.takeWhile { it >= 0 } // a -1 signals end of the dataset (break)
.map { it + 1 } // increment each number
.filterNot { it % 5 == 0 } // skip (continue) numbers divisible by 5
.map { it - 1 } // decrement each number by 1
.filter { it < 100 } // skip (continue) if number is >= 100
.drop(5) // ignore the first 5 numbers
.take(10) // use the next 10 numbers and end
.forEach {
// do work on the final list
}
Une combinaison de ces fonctions a tendance à éliminer le besoin de continue
ou break
. Et il existe d'innombrables options différentes ici et plus que ce qui peut être documenté. Pour avoir une idée de ce qui peut être fait, il est préférable d'apprendre toutes les fonctions disponibles dans la bibliothèque standard de Kotlin pour collections , paresseux séquences et itérable .
Parfois, il existe des cas où vous avez un état de mutation qui doit encore break
ou continue
et est difficile à faire dans un modèle fonctionnel. Vous pouvez le faire fonctionner en utilisant des fonctions plus complexes comme fold
et reduce
combinées avec les fonctions filter
et takeWhile
mais parfois c'est plus difficile à comprendre. Par conséquent, si vous voulez vraiment ce comportement exact, vous pouvez utiliser retour de l'expression lambda qui imite un continue
ou break
selon votre utilisation.
Voici un exemple imitant continue
:
(1..5).forEach {
if (it == 3) return@forEach // mimic continue@forEach
// ... do something more
}
Et vous pouvez aller plus compliqué et utiliser des étiquettes lorsque vous rencontrez des situations d'imbrication ou de confusion:
(1..3).forEach outer@ { x ->
(1..3).forEach inner@ { y ->
if (x == 2 && y == 2) return@outer // mimic continue@outer
if (x == 1 && y == 1) return@inner // mimic continue@inner
// ... do something more
}
}
Si vous voulez faire un break
vous avez besoin de quelque chose en dehors de la boucle que vous pouvez retourner, ici nous utiliserons la fonction run()
pour nous aider:
run breaker@ {
(1..20).forEach { x ->
if (x == 5) return@breaker // mimic break@forEach
// ... do something more
}
}
Au lieu de run()
, il peut s'agir de let()
ou apply()
ou de tout ce que vous avez naturellement autour du forEach
qui est un endroit dont vous voulez vous séparer. Mais vous sauterez également le code dans le même bloc après le forEach
alors soyez prudent.
Ce sont des fonctions intégrées, donc elles n'ajoutent pas vraiment de surcharge.
Lisez les documents de référence de Kotlin pour Retours et sauts pour tous les cas spéciaux, y compris pour les fonctions anonymes.
Voici un test unitaire prouvant que tout fonctionne:
@Test fun testSo32540947() {
val results = arrayListOf<Pair<Int,Int>>()
(1..3).forEach outer@ { x ->
(1..3).forEach inner@ { y ->
if (x == 2 && y == 2) return@outer // continue @outer
if (x == 1 && y == 1) return@inner // continue @inner
results.add(Pair(x,y))
}
}
assertEquals(listOf(Pair(1,2), Pair(1,3), Pair(2,1), Pair(3,1), Pair(3,2), Pair(3,3)), results)
val results2 = arrayListOf<Int>()
run breaker@ {
(1..20).forEach { x ->
if (x == 5) return@breaker
results2.add(x)
}
}
assertEquals(listOf(1,2,3,4), results2)
}
takeWhile La fonction stdlib peut être utilisée à la place de break.
Par exemple,
val array = arrayOf(2, 8, 4, 5, 13, 12, 16)
array.takeWhile { it % 2 == 0 }.forEach { println(it) } // break on odd
array.takeWhile { it % 3 != 0 }.forEach { println(it) } // break on 3 * n