`break` dan` continue` di `forEach` di Kotlin

120

Kotlin memiliki fungsi iterasi yang sangat bagus, like forEachor repeat, tetapi saya tidak dapat membuat operator breakand continuebekerja dengannya (baik lokal maupun non-lokal):

repeat(5) {
    break
}

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

Tujuannya adalah untuk meniru loop biasa dengan sintaks fungsional sedekat mungkin. Itu pasti mungkin di beberapa versi lama Kotlin, tapi saya kesulitan untuk mereproduksi sintaksnya.

Masalahnya mungkin bug dengan label (M12), tetapi saya pikir contoh pertama harus berfungsi.

Sepertinya saya pernah membaca di suatu tempat tentang trik / anotasi khusus, tetapi saya tidak dapat menemukan referensi apa pun tentang subjek tersebut. Mungkin terlihat seperti berikut:

public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
    for (index in 0..times - 1) {
        body(index)
    }
}
voddan
sumber
1
Di Kotlin saat ini, Anda memang dapat meniru ini (sambil menunggu continue@labeldan break@labelfitur), lihat pertanyaan terkait: stackoverflow.com/questions/34642868/…
Jayson Minard
1
Pertanyaan ini dapat menggunakan klarifikasi tentang apakah Anda hanya menanyakan tentang keberadaan breakdan continueuntuk loop fungsional, atau jika Anda mencari jawaban alternatif yang melakukan hal yang persis sama. Yang pertama tampaknya menjadi masalah, karena Anda menolak yang terakhir.
Jayson Minard
tampaknya mereka ditambahkan bahwa di kotlin 1.3
Tigran Babajanyan
@TigranBabajanyan wow! Apakah kamu punya link
voddan
@voddan, tidak, saya baru saja mencobanya
Tigran Babajanyan

Jawaban:

68

Edit :
Menurut dokumentasi Kotlin , dimungkinkan menggunakan anotasi.

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

Jawaban Asli :
Karena Anda memberikan a (Int) -> Unit, Anda tidak dapat memutuskannya, karena kompilator tidak tahu bahwa itu digunakan dalam satu loop.

Anda memiliki beberapa pilihan:

Gunakan loop biasa:

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

Jika loop adalah kode terakhir dalam metode
yang dapat Anda gunakan returnuntuk keluar dari metode (atau return valuejika itu bukan unitmetode).

Gunakan metode
Buat metode pengulangan kustom yang kembali Booleanuntuk melanjutkan.

public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
    for (index in 0 until times) {
        if (!body(index)) break
    }
}
Yoav Sternberg
sumber
Sebenarnya, pertanyaan saya adalah tentang membuat sintaks tertentu berfungsi, bukan tentang pengulangan. Tidakkah Anda ingat bahwa hal itu mungkin terjadi pada beberapa pencapaian Kotlin?
voddan
1
Saya tidak ingat. Tapi mungkin itu karena saya tidak menggunakan istirahat & terus banyak. Lihat masalah ini , dikatakan "Estimasi - Tidak ada estimasi".
Yoav Sternberg
1
breakdan continuehanya bekerja dalam loop. forEach, repeatdan semua metode lainnya hanya itu: metode dan bukan loop. Yoav mempresentasikan beberapa alternatif tetapi breakdan continuetidak hanya bekerja untuk metode.
Kirill Rakhman
@YoavSternberg Brilian! Kedamaian dokumen lama inilah yang saya cari! Jadi fitur tersebut belum diimplementasikan, tersisa untuk versi yang akan datang. Jika Anda ingin membuat jawaban terpisah, saya akan menandainya
voddan
Di Kotlin saat ini, Anda memang dapat meniru ini (sambil menunggu continue@labeldan break@labelfitur), lihat pertanyaan terkait: stackoverflow.com/questions/34642868/…
Jayson Minard
104

Ini akan mencetak 1 sampai 5. return@forEachBertindak seperti kata kunci continuedi Java, yang berarti dalam kasus ini, ia masih mengeksekusi setiap loop tetapi melompat ke iterasi berikutnya jika nilainya lebih besar dari 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)
    }
}

Ini akan mencetak 1 hingga 10 tetapi melompati 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)
    }
}

Cobalah di Kotlin Playground .

pengusaha licik
sumber
Bagus, tapi ini masih belum mengatasi masalah karena tidak dapat mengakhiri forEach sebelum waktunya ketika beberapa kondisi terpenuhi. Itu masih terus mengeksekusi loop.
The Fox
1
@TheFox ya, itu mengeksekusi setiap loop dan apa pun setelah pengembalian dilewati ketika kondisi terpenuhi. Setiap operasi di forEach adalah fungsi lambda, saat ini tidak ada operasi pemutusan yang tepat untuk operasi forEach. Istirahat tersedia untuk loop, lihat: kotlinlang.org/docs/reference/returns.html
s-hunter
Berikut cuplikan Kotlin Playground yang dapat dijalankan dengan a continuedan breakcontoh: pl.kotl.in/_LAvET-wX
ashughes
34

Istirahat dapat dicapai dengan menggunakan:

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

Dan kelanjutan dapat dicapai dengan:

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

Seperti yang direkomendasikan oleh siapa pun di sini ... baca dokumen: P https://kotlinlang.org/docs/reference/returns.html#return-at-labels

Raymond Arteaga
sumber
Solusi bagus. Bekerja dengan sangat baik. Meskipun sepertinya tanpa menggunakan @loopmemberikan hasil yang diinginkan sama juga.
Paras Sidhu
Nyatanya, Anda dapat menghilangkan tag eksplisit "@loop" dan menggunakan tag implisit "@run". Aspek kuncinya di sini adalah kembalinya lokal ke pemanggil lambda. Perhatikan bahwa Anda perlu membungkus loop di dalam beberapa cakupan sehingga Anda dapat mengembalikannya secara lokal nanti.
Raymond Arteaga
17

Seperti yang dikatakan dalam dokumentasi Kotlin , menggunakan returnadalah cara yang tepat. Hal yang baik tentang kotlin adalah jika Anda memiliki fungsi bertingkat, Anda dapat menggunakan label untuk secara eksplisit menulis dari mana asal pengembalian Anda:

Pengembalian Lingkup Fungsi

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

dan Pengembalian Lokal (tidak berhenti melalui forEach = lanjutan)

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

Lihat dokumentasinya, bagus banget :)

cesards
sumber
3
Peringatan: return @ lit tidak berhentiforEach
Jemshit Iskenderov
Itu betul. Ini dimaksudkan. Solusi pertama memang demikian, tetapi jika Anda memiliki instruksi di dalam loop, Anda dapat memilih ke mana Anda ingin kembali ke / lompat. Dalam kasus kedua, jika kita hanya menggunakan return maka akan berhenti ;-)
cesards
Memanggil Kembali @ menyala suka Lanjutkan
pqtuan86
10

continue ketik perilaku dalam 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
}

untuk breakjenis perilaku yang harus Anda gunakan for in untilatau for insesuai daftar adalah NullableatauNon-Nullable

  1. Untuk daftar 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. Untuk daftar Non-Nullable :

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

Pernyataan break untuk loop bersarang forEach ():

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

Hasil:

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

Lanjutkan pernyataan dengan fungsi anonim:

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

Hasil:

1 2 4 5 
alexrnov.dll
sumber
0

mungkin mengubah forEach menjadi

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

ini berfungsi untuk hashmaps

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

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