`break` en `continue` in `forEach` in Kotlin

Kotlin heeft hele leuke iteratiefuncties, zoals forEachof repeat, maar ik kan de breaken continueoperators werken met hen samen (zowel lokaal als niet-lokaal):

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

Het doel is om gebruikelijke lussen na te bootsen met de functionele syntaxis zo dicht mogelijk. Het was zeker mogelijk in sommige oudere versies van Kotlin, maar ik heb moeite om de syntaxis te reproduceren.

Het probleem kan een bug zijn met labels (M12), maar ik denk dat het eerste voorbeeld toch zou moeten werken.

Het lijkt me dat ik ergens gelezen heb over een speciale truc/annotatie, maar ik kon geen referentie over het onderwerp vinden. Zou er als volgt uit kunnen zien:

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

Antwoord 1, autoriteit 100%

Hiermee wordt 1 tot 5 afgedrukt. De return@forEachwerkt als het trefwoord continuein Java, wat in dit geval betekent dat het nog steeds elke lus uitvoert, maar naar de volgende iteratie als de waarde groter is dan 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)
    }
}

Hiermee drukt u 1 tot 10 af, maar slaat 5 over.

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)
    }
}

Probeer ze op Kotlin Playground.


Antwoord 2, autoriteit 95%

Bewerken:
Volgens Kotlin’s documentatieis het mogelijk om continuete simuleren met annotaties.

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")
}

Als u een breakwilt simuleren, voegt u gewoon een run-blok

toe

fun foo() {
    run lit@ {
        listOf(1, 2, 3, 4, 5).forEach {
            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")
    }
}

Oorspronkelijk antwoord:
Aangezien u een (Int) -> Unit, je kunt er niet uit breken, omdat de compiler niet weet dat het in een lus wordt gebruikt.

Je hebt weinig opties:

Gebruik een normale for-lus:

for (index in 0 until times) {
    // your code here
}

Als de lus de laatste code in de methode is
je kunt returngebruiken om uit de methode te komen (of return valueals het geen unitmethode is).

Gebruik een methode
Maak een aangepaste herhaalmethode die Booleanretourneert om door te gaan.

public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
    for (index in 0 until times) {
        if (!body(index)) break
    }
}

Antwoord 3, autoriteit 39%

Een pauze kan worden bereikt met:

//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")
}

En een continuering kan worden bereikt met:

//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")
}

Zoals iedereen hier aanbeveelt… lees de documenten 😛
https://kotlinlang.org/docs/reference/returns.html# return-at-labels

BEWERKEN:
Hoewel de hoofdvraag over forEach gaat, is het belangrijk om de goede oude “voor” in overweging te nemen. Het gebruik van Kotlin betekent niet dat we forEach altijd moeten gebruiken. Het goede oude “voor” gebruiken is prima, en soms zelfs expressiever en beknopter dan voor elk:

fun foo() {
    for(x in listOf(1, 2, 3, 4, 5){
        if (x == 3) break //or continue
        print(x)
    }
    print("done with the good old for")
}

Antwoord 4, autoriteit 18%

U kunt return from lambda-expressiegebruiken die een continueof breakafhankelijk van je gebruik.

Dit wordt behandeld in de gerelateerde vraag: Hoe maak ik een “break” of “continue” in een functionele loop binnen Kotlin?


Antwoord 5, autoriteit 15%

Zoals de Kotlin-documentatie zegt, is het gebruik van returnde weg te gaan. Het goede aan kotlin is dat als je geneste functies hebt, je labels kunt gebruiken om expliciet te schrijven waar je terugkeer vandaan komt:

Functiebereik rendement

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")
}

en
Lokale terugkeer(het stopt niet met doorlopen forEach = vervolg)

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")
}

Bekijk de documentatie, het is echt goed 🙂


Antwoord 6, autoriteit 9%

continuetyp gedrag in 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
}

voor gedrag van het type breakmoet je for in untilof for ingebruiken volgens de lijst is Nullableof Non-Nullable

  1. Voor Nullablelijst:

    for (index in 0 until list.size) {
        val item = list[index] // you can use data item now
        if () {
            // your code
            break
        }
        // your code
    }
    
  2. Voor Non-Nullablelijst:

    for (item in list) { // data item will available right away
        if () {
            // your code
            break
        }
        // your code
    }
    

Antwoord 7, autoriteit 3%

Ik heb hier de perfecte oplossing voor (:

list.apply{ forEach{ item ->
    if (willContinue(item)) return@forEach
    if (willBreak(item)) return@apply
}}

Antwoord 8

Break-instructie voor geneste lussen forEach():

listOf("a", "b", "c").forEach find@{ i ->
    listOf("b", "d").forEach { j ->
        if (i == j) return@find
        println("i = $i, j = $j")
    }
}

Resultaat:

i = a, j = b
i = a, j = d
i = c, j = b
i = c, j = d

Continue statement met anonieme functie:

listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
    if (value == 3) return
    print("$value ")
})

Resultaat:

1 2 4 5 

Antwoord 9

 fun part2(ops: List<Int>): Int = ops.asSequence()
    .scan(0) { acc, v -> acc + v }
    .indexOf(-1)

Als je het je kunt veroorloven om van een verzameling een sequencete maken, zijn de kosten normaal gesproken onbeduidend, dan zou je moeten kunnen profiteren van de uitgestelde functie.

Misschien zie je asSequenceal in het bovenstaande. Het is hier om ons te redden door de hele lijst door te nemen. Direct nadat we een match hebben via indexOf, stopt deze. Bingo! Schrijf ons een whilehier.

zoals in deel 2 van https://medium .com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1


Antwoord 10

misschien verander forEach naar

for(it in myList){
   if(condition){
     doSomething()
   }else{
     break //or continue
    }
} 

het werkt voor hashmaps

for(it in myMap){
     val k = it.key
     val v = it.value
       if(condition){
         doSomething()
       }else{
         break //or continue
        }
    }

Other episodes