"break" e "continue" in "forEach" in Kotlin


120

Kotlin ha funzioni di iterazione molto carine, come forEacho repeat, ma non sono in grado di far funzionare gli operatori breake continue(sia locali che non locali):

repeat(5) {
    break
}

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

L'obiettivo è imitare i soliti loop con la sintassi funzionale il più vicino possibile. Era sicuramente possibile in alcune versioni precedenti di Kotlin, ma faccio fatica a riprodurre la sintassi.

Il problema potrebbe essere un bug con le etichette (M12), ma penso che il primo esempio dovrebbe funzionare comunque.

Mi sembra di aver letto da qualche parte su uno speciale trucco / annotazione, ma non sono riuscito a trovare alcun riferimento sull'argomento. Potrebbe assomigliare al seguente:

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

1
Nell'attuale Kotlin puoi davvero imitare questo (in attesa delle funzionalità continue@labele break@label), vedi la domanda correlata: stackoverflow.com/questions/34642868/…
Jayson Minard

1
Questa domanda potrebbe essere utile per chiarire se stai chiedendo solo l'esistenza breake continueper i cicli funzionali, o se stai cercando risposte alternative che fanno esattamente la stessa cosa. Il primo sembra essere il caso, perché hai rifiutato il secondo.
Jayson Minard

sembra che si aggiunga che in kotlin 1.3
Tigran Babajanyan

@TigranBabajanyan wow! Avete un link?
Voddan

@voddan, no, ho appena provato che funziona
Tigran Babajanyan

Risposte:


68

Modifica :
secondo la documentazione di Kotlin , è possibile utilizzare le annotazioni.

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

Risposta originale :
poiché fornisci a (Int) -> Unit, non puoi interromperlo, poiché il compilatore non sa che viene utilizzato in un ciclo.

Hai poche opzioni:

Usa un ciclo for regolare:

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

Se il ciclo è l'ultimo codice nel metodo
che puoi usare returnper uscire dal metodo (o return valuese non è unitmetodo).

Usa un metodo
Crea un metodo di ripetizione personalizzato che ritorni Booleanper continuare.

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

In realtà, la mia domanda riguardava il funzionamento della sintassi specifica, non l'iterazione. Non ti ricordi che era possibile in qualche pietra miliare di Kotlin?
voddan

1
Non ricordo Ma forse è perché non uso molto break & continue. Vedi questo problema , dice "Stima - Nessuna stima".
Yoav Sternberg

1
breake continuefunzionano solo in loop. forEach, repeatE tutti gli altri metodi sono proprio questo: i metodi e non loop. Yoav presentato alcune alternative, ma breake continuenon sono solo Ment al lavoro per i metodi.
Kirill Rakhman

@YoavSternberg Brilliant! Questa pace di vecchi documenti è quello che stavo cercando! Quindi la funzione non è ancora implementata, lasciata per versioni future. Se ti interessa creare una risposta separata, la contrassegnerò
voddan

Nell'attuale Kotlin puoi davvero imitare questo (in attesa delle funzionalità continue@labele break@label), vedi la domanda correlata: stackoverflow.com/questions/34642868/…
Jayson Minard

104

Verrà stampato da 1 a 5. return@forEachAgisce come la parola chiave continuein Java, il che significa che in questo caso esegue ancora ogni ciclo ma salta all'iterazione successiva se il valore è maggiore di 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)
    }
}

Questo stamperà da 1 a 10 ma salterà 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)
    }
}

Provali al Kotlin Playground .


Ottimo, ma questo ancora non risolve il problema di non essere in grado di terminare prematuramente il forEach quando viene soddisfatta una determinata condizione. Continua ancora a eseguire il ciclo.
The Fox

1
@TheFox sì, esegue ogni ciclo e qualsiasi cosa dopo il ritorno viene saltata quando la condizione è soddisfatta. Ogni operazione in forEach è una funzione lambda, attualmente non esiste alcuna operazione di interruzione esatta per l'operazione forEach. La pausa è disponibile nei cicli for, vedere: kotlinlang.org/docs/reference/returns.html
s-hunter

Ecco un eseguibile Kotlin giochi snippet sia con una continuee breakEsempio: pl.kotl.in/_LAvET-wX
ashughes

34

Una pausa può essere ottenuta utilizzando:

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

E si può ottenere una continuazione con:

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

Come chiunque qui consiglia ... leggi i documenti: P https://kotlinlang.org/docs/reference/returns.html#return-at-labels


Bella soluzione. Funziona molto bene. Anche se sembra che senza usare si @loopottenga lo stesso risultato desiderato.
Paras Sidhu

Puoi infatti omettere il tag esplicito "@loop" e utilizzare quello implicito "@run". L'aspetto chiave qui è il ritorno locale al chiamante del lambda. Nota che devi avvolgere il ciclo all'interno di uno scope in modo da poterlo tornare localmente in seguito.
Raymond Arteaga


17

Come dice la documentazione di Kotlin , l'utilizzo returnè la strada da percorrere. La cosa buona di kotlin è che se hai funzioni annidate, puoi usare le etichette per scrivere esplicitamente da dove proviene il tuo ritorno:

Funzione Scope Return

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

e Local Return (non smette di andare avanti per ogni = continuazione)

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

Controlla la documentazione, è davvero buona :)


3
Attenzione: return @ lit non si fermaforEach
Jemshit Iskenderov

È corretto. È inteso. La prima soluzione lo fa, ma se hai istruzioni all'interno di un ciclo puoi scegliere dove vuoi tornare / saltare. Nel secondo caso, se usiamo solo return, si fermerà ;-)
cesards

Calling Return @ lit likes Continua
pqtuan86

10

continue digitare il comportamento 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
}

per il breaktipo di comportamento che devi usare for in untilo for insecondo l'elenco è NullableoNon-Nullable

  1. Per l' elenco nullable :

    for (index in 0 until list.size) {
        val item = list[index] // you can use data item now
        if () {
            // your code
            break
        }
    
        // your code
    }
  2. Per l' elenco non annullabile :

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

2

Istruzione Break per cicli annidati forEach ():

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

Risultato:

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

Continua istruzione con funzione anonima:

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

Risultato:

1 2 4 5 

0

forse cambiare per ciascuno

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

funziona per hashmap

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

       if(condition){
         doSomething()
       }else{
         break //or continue
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.