In Kotlin kan ik geen break
of continue
doen binnen een functielus en mijn lambda — zoals ik kan met een normale for
lus. Dit werkt bijvoorbeeld niet:
(1..5).forEach {
continue@forEach // not allowed, nor break@forEach
}
Er zijn oude documentatiewaarin wordt vermeld dat dit beschikbaar, maar het lijkt erop dat het nooit is geïmplementeerd. Wat is de beste manier om hetzelfde gedrag te krijgen als ik vanuit de lambda wil continue
of break
?
Opmerking:deze vraag is met opzet geschreven en beantwoord door de auteur (Zelfbeantwoorde vragen), zodat de idiomatische antwoorden op veelgestelde Kotlin-onderwerpen aanwezig zijn in SO. Ook om enkele echt oude antwoorden te verduidelijken die zijn geschreven voor alfa’s van Kotlin die niet nauwkeurig zijn voor het huidige Kotlin.
Antwoord 1, autoriteit 100%
Er zijn andere opties dan waar u om vraagt die vergelijkbare functionaliteit bieden. Bijvoorbeeld:
Je kunt het verwerken van sommige waarden vermijden met behulp van filter
: (like a continue
)
dataSet.filter { it % 2 == 0 }.forEach {
// do work on even numbers
}
Je kunt een functionele lus stoppen door takeWhile
: (als een break
)
dataSet.takeWhile { it < 10 }.forEach {
// do work on numbers as long as they are < 10, otherwise stop
}
Een complexer, hoewel onzinnig voorbeeld waarbij u wat verwerking wilt doen, enkele resulterende waarden wilt overslaan en vervolgens wilt stoppen bij een reeks verschillende voorwaarden, zou zijn:
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
}
Een combinatie van deze functies heeft de neiging om continue
of break
te elimineren. En er zijn hier eindeloos veel verschillende opties en meer dan kan worden gedocumenteerd. Om een idee te krijgen van wat er gedaan kan worden, kunt u het beste alle functies leren die beschikbaar zijn in de Kotlin-standaardbibliotheek voor collecties, luie reeksenen itereerbaar.
Soms zijn er gevallen waarin je een muterende toestand hebt die nog moet break
of continue
en dat is moeilijk te doen in een functioneel model. Je kunt het laten werken met complexere functies zoals fold
en reduce
in combinatie met de functies filter
en takeWhile
, maar soms dat is moeilijker te grommen. Daarom, als je echt dat exacte gedrag wilt, kun je return from lambda expressiondie een continue
of break
nabootst, afhankelijk van je gebruik.
Hier is een voorbeeld dat continue
nabootst:
(1..5).forEach {
if (it == 3) return@forEach // mimic continue@forEach
// ... do something more
}
En je kunt ingewikkelder gaan en labels gebruiken als je nesten of verwarrende situaties hebt:
(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
}
}
Als je een break
wilt doen, heb je iets buiten de lus nodig waarvan je kunt terugkeren, hier zullen we de functie run()
gebruiken om ons te helpen:
run breaker@ {
(1..20).forEach { x ->
if (x == 5) return@breaker // mimic break@forEach
// ... do something more
}
}
In plaats van run()
zou het let()
of apply()
kunnen zijn of iets natuurlijks dat je hebt rond de forEach
dat is een plek waar je wilt breken. Maar je slaat ook de code over binnen hetzelfde blok na de forEach
dus wees voorzichtig.
Dit zijn inline-functies, dus ze voegen niet echt overhead toe.
Lees de Kotlin-referentiedocumenten voor Retouren en sprongenvoor alle speciale gevallen, inclusief voor anonieme functies.
Hier is een eenheidstestdie aantoont dat dit allemaal werkt:
@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)
}
Antwoord 2, autoriteit 2%
takeWhilestdlib-functie kan gebruikt in plaats van pauze.
Bijvoorbeeld
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
Antwoord 3
forEach
met pauze kan specifiek worden vervangen door elkefunctie:
(1..20).any { x ->
(x == 5).apply { // break on true
if (!this) {
results2.add(x)
}
}
}
Of mogelijk nog korter:
(1..20).any { x ->
results2.add(x)
x == 4 // break on true
}