`break` und` continue` in` forEach` in Kotlin

118

Kotlin hat sehr schöner Iterieren Funktionen, wie forEachoder repeat, aber ich bin nicht in der Lage das zu machen breakund continuemit ihnen Operatoren arbeiten (sowohl lokale als auch nicht-lokal):

repeat(5) {
    break
}

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

Das Ziel ist es, übliche Schleifen mit der funktionalen Syntax so nah wie möglich nachzuahmen. In einigen älteren Versionen von Kotlin war dies definitiv möglich, aber ich habe Schwierigkeiten, die Syntax zu reproduzieren.

Das Problem könnte ein Fehler mit Labels (M12) sein, aber ich denke, dass das erste Beispiel trotzdem funktionieren sollte.

Es scheint mir, dass ich irgendwo über einen speziellen Trick / eine spezielle Anmerkung gelesen habe, aber ich konnte keine Referenz zu diesem Thema finden. Könnte wie folgt aussehen:

public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
    for (index in 0..times - 1) {
        body(index)
    }
}
Voddan
quelle
1
Im aktuellen Kotlin können Sie dies tatsächlich nachahmen (während Sie auf die continue@labelund break@labelFunktionen warten ), siehe verwandte Frage: stackoverflow.com/questions/34642868/…
Jayson Minard
1
Diese Frage könnte klargestellt werden, ob Sie nur nach der Existenz von breakund continuefür Funktionsschleifen fragen oder ob Sie nach alternativen Antworten suchen, die genau dasselbe tun. Ersteres scheint der Fall zu sein, weil Sie Letzteres abgelehnt haben.
Jayson Minard
es scheint, dass sie hinzugefügt werden, dass in Kotlin 1.3
Tigran Babajanyan
@TigranBabajanyan wow! Hast du einen Link?
Voddan
@ Voddan, nein, ich habe gerade versucht, es funktioniert
Tigran Babajanyan

Antworten:

67

Bearbeiten :
Laut Kotlins Dokumentation ist es möglich, Anmerkungen zu verwenden.

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

Ursprüngliche Antwort :
Da Sie a angeben (Int) -> Unit, können Sie nicht davon abbrechen, da der Compiler nicht weiß, dass es in einer Schleife verwendet wird.

Sie haben wenige Möglichkeiten:

Verwenden Sie eine reguläre for-Schleife:

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

Wenn die Schleife der letzte Code in der Methode ist
, können Sie returndie Methode verlassen (oder return valuewenn es sich nicht um eine unitMethode handelt).

Verwenden einer Methode
Erstellen Sie eine benutzerdefinierte Methode Booleanfür Wiederholungsmethoden, die zum Fortfahren zurückgegeben wird.

public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
    for (index in 0 until times) {
        if (!body(index)) break
    }
}
Yoav Sternberg
quelle
Eigentlich ging es mir darum, die spezifische Syntax zum Laufen zu bringen, nicht um das Iterieren. Erinnerst du dich nicht, dass es bei einem Kotlin-Meilenstein möglich war?
Voddan
1
Ich erinnere mich nicht. Aber vielleicht liegt es daran, dass ich break & nicht viel benutze. In dieser Ausgabe steht "Schätzung - Keine Schätzung".
Yoav Sternberg
1
breakund arbeiten continuenur in Schleifen. forEach, repeatUnd alle anderen Methoden sind genau das: Methoden und nicht - Schleifen. Yoav präsentierte einige Alternativen aber breakund continuesind einfach nicht an die Arbeit für Methoden ment.
Kirill Rakhman
@ YoavSternberg Genial! Dieser Frieden alter Dokumente ist das, wonach ich gesucht habe! Daher ist die Funktion noch nicht implementiert und für zukünftige Versionen verfügbar. Wenn Sie eine separate Antwort erstellen
möchten
Im aktuellen Kotlin können Sie dies tatsächlich nachahmen (während Sie auf die continue@labelund break@labelFunktionen warten ), siehe verwandte Frage: stackoverflow.com/questions/34642868/…
Jayson Minard
103

Dies gibt 1 bis 5 aus. Das return@forEachVerhalten verhält sich wie das Schlüsselwort continuein Java, was in diesem Fall bedeutet, dass immer noch jede Schleife ausgeführt wird, aber zur nächsten Iteration gesprungen wird, wenn der Wert größer als 5 ist.

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

Dies gibt 1 bis 10 aus, überspringt jedoch 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)
    }
}

Probieren Sie sie auf dem Kotlin Playground aus .

Rangierer
quelle
Großartig, aber dies behebt immer noch nicht das Problem, dass forEach nicht vorzeitig beendet werden kann, wenn eine Bedingung erfüllt ist. Die Schleife wird weiterhin ausgeführt.
Der Fuchs
1
@TheFox ja, es führt jede Schleife und alles aus, was nach der Rückgabe übersprungen wird, wenn die Bedingung erfüllt ist. Jede Operation in forEach ist eine Lambda-Funktion. Derzeit gibt es keine genaue Unterbrechungsoperation für die forEach-Operation. Die Pause ist für Schleifen verfügbar, siehe: kotlinlang.org/docs/reference/returns.html
s-hunter
Hier ist ein lauffähiges Kotlin Playground-Snippet mit einem continueund einem breakBeispiel: pl.kotl.in/_LAvET-wX
ashughes
34

Eine Pause kann erreicht werden mit:

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

Und eine Fortsetzung kann erreicht werden mit:

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

Wie hier jeder empfiehlt ... lesen Sie die Dokumente: P https://kotlinlang.org/docs/reference/returns.html#return-at-labels

Raymond Arteaga
quelle
Schöne Lösung. Funktioniert sehr gut. Obwohl es so aussieht, als würde ohne Verwendung auch @loopdas gleiche gewünschte Ergebnis erzielt.
Paras Sidhu
Tatsächlich können Sie das explizite Tag "@loop" weglassen und das implizite "@run" verwenden. Der Schlüsselaspekt hier ist die lokale Rückkehr zum Anrufer des Lambda. Beachten Sie, dass Sie die Schleife in einen bestimmten Bereich einschließen müssen, damit Sie sie später lokal zurückgeben können.
Raymond Arteaga
17

Wie in der Kotlin-Dokumentation angegeben , returnist die Verwendung der richtige Weg. Das Gute an Kotlin ist, dass Sie, wenn Sie verschachtelte Funktionen haben, Labels verwenden können, um explizit zu schreiben, woher Ihre Rückkehr stammt:

Rückgabe des Funktionsumfangs

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

und Local Return (es hört nicht auf, forEach = Fortsetzung durchzugehen)

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

Kasse die Dokumentation, es ist wirklich gut :)

cesards
quelle
3
Warnung: return @ lit hört nicht aufforEach
Jemshit Iskenderov
Das ist richtig. Es ist beabsichtigt. Die erste Lösung, die es gibt, aber wenn Sie Anweisungen in einer Schleife haben, können Sie auswählen, wohin Sie zurückkehren / springen möchten. Im zweiten Fall, wenn wir nur return verwenden, wird es aufhören ;-)
cesards
Calling Return @ lit gefällt weiter
pqtuan86
9

continue Verhalten eingeben 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
}

Für das breakTypverhalten müssen Sie verwenden for in untiloder for ingemäß der Liste ist NullableoderNon-Nullable

  1. Für nullbare Liste:

    for (index in 0 until list.size) {
        val item = list[index] // you can use data item now
        if () {
            // your code
            break
        }
    
        // your code
    }
    
  2. Für nicht nullbare Liste:

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

Break-Anweisung für verschachtelte Schleifen forEach ():

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

Ergebnis:

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

Fortsetzung der Anweisung mit anonymer Funktion:

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

Ergebnis:

1 2 4 5 
alexrnov
quelle
0

vielleicht für jeden ändern

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

Es funktioniert für Hashmaps

 for(it in myMap){
     val k = it.key
     val v = it.value

       if(condition){
         doSomething()
       }else{
         break //or continue
        }
    }
nexDev
quelle