Kotlin'de "forEach" için "break" ve "Continue"


122

Kotlin, forEachveya gibi çok güzel yineleme işlevlerine sahiptir repeat, ancak breakve continueoperatörlerinin onlarla çalışmasını sağlayamıyorum (hem yerel hem de yerel olmayan):

repeat(5) {
    break
}

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

Amaç, olağan döngüleri olabildiğince yakın işlevsel sözdizimi ile taklit etmektir. Kotlin'in bazı eski sürümlerinde kesinlikle mümkündü, ancak sözdizimini yeniden üretmekte zorlanıyorum.

Sorun etiketli bir hata olabilir (M12), ancak yine de ilk örneğin işe yarayacağını düşünüyorum.

Bana öyle geliyor ki bir yerde özel bir numara / açıklama hakkında okudum, ancak konuyla ilgili herhangi bir referans bulamadım. Aşağıdaki gibi görünebilir:

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

1
Mevcut Kotlin'de bunu gerçekten taklit edebilirsiniz ( continue@labelve break@labelözellikleri beklerken ), ilgili soruya bakın: stackoverflow.com/questions/34642868/…
Jayson Minard

1
Bu soru, yalnızca işlevsel döngülerin varlığını breakve varlığını sorup sormadığınızı continueveya tam olarak aynı şeyi yapan alternatif yanıtlar mı arıyorsunuz? İlki öyle görünüyor, çünkü siz ikincisini reddettiniz.
Jayson Minard

Görünüşe göre kotlin 1.3
Tigran Babajanyan

@TigranBabajanyan vay! Bağlantınız var mı?
voddan

@voddan, hayır, işe yaramasını denedim
Tigran Babajanyan

Yanıtlar:


69

Düzenleme :
Kotlin'in belgelerine göre , ek açıklamalar kullanmak mümkündür.

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

Orijinal Cevap :
a (Int) -> Unitsağladığınız için, bundan kopamazsınız, çünkü derleyici bir döngüde kullanıldığını bilmiyor.

Birkaç seçeneğiniz var:

Normal bir for döngüsü kullanın:

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

Döngü yöntemdeki son kodsa, yöntemden çıkmak
için kullanabilirsiniz return(veya yöntem return valuedeğilse unit).

Bir yöntem kullanın Devam
etmek için geri dönen özel bir tekrar yöntemi oluşturun Boolean.

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

Aslında sorum, yinelemeyle ilgili değil, belirli sözdiziminin çalışmasıyla ilgiliydi. Bazı Kotlin dönüm noktalarında bunun mümkün olduğunu hatırlamıyor musun?
voddan

1
Ben hatırlamıyorum Ama belki ara kullanmadığım ve çok fazla devam etmediğim içindir. Bu sorunu görün , "Tahmin - Tahmin yok" yazıyor.
Yoav Sternberg

1
breakve continueyalnızca döngülerde çalışın. forEach, repeatYöntemler ve döngüler: ve tüm diğer yöntemler vardır sadece. Yoav bazı alternatifler sunuldu ancak breakve continuesadece yöntemleri için işe ment değildir.
Kirill Rakhman

@YoavSternberg Harika! Eski doktorların bu huzuru aradığım şeydi! Yani özellik henüz uygulanmadı, gelecekteki sürümler için bırakıldı. Eğer ayrı bir cevap oluşturmaya önem veriyorsanız, bunu işaretlemek edeceğiz
voddan

Mevcut Kotlin'de bunu gerçekten taklit edebilirsiniz ( continue@labelve break@labelözellikleri beklerken ), ilgili soruya bakın: stackoverflow.com/questions/34642868/…
Jayson Minard

106

Bu, 1'den 5'e kadar yazdırır . Java'daki return@forEachanahtar sözcük gibi davranır continue, yani bu durumda, yine de her döngüyü yürütür ancak değer 5'ten büyükse sonraki yinelemeye atlar.

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

Bu, 1'den 10'a kadar yazdırır ancak 5'i atlar.

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

Bunları Kotlin Playground'da deneyin .


Harika, ama bu hala bazı koşullar karşılandığında forEach'i erken sonlandıramama sorununu çözmüyor. Hala döngüyü yürütmeye devam ediyor.
The Fox

1
@TheFox evet, her döngüyü çalıştırır ve koşul karşılandığında dönüşten sonraki her şey atlanır. ForEach'deki her işlem bir lambda işlevidir, şu anda forEach işlemi için kesin bir kesme işlemi yoktur. Ara, döngüler için mevcuttur, bakınız: kotlinlang.org/docs/reference/returns.html
s-hunter

İşte çalıştırılabilir Kotlin Bahçesi hem a ile pasajı var continueve break: Örneğin pl.kotl.in/_LAvET-wX
ashughes

36

Aşağıdakiler kullanılarak bir mola elde edilebilir:

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

Ve şu şekilde devam edilebilir:

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

Buradaki herkesin önerdiği gibi ... belgeleri okuyun: P https://kotlinlang.org/docs/reference/returns.html#return-at-labels


Güzel çözüm. Çok iyi çalışıyor. Kullanmadan gibi görünse @loopde aynı istenen sonucu verir.
Paras Sidhu

Aslında, "@loop" açık etiketini çıkarabilir ve örtük olan "@run" etiketini kullanabilirsiniz. Buradaki anahtar husus, lambda'yı arayan kişiye yerel dönüştür. Döngüyü bir kapsamın içine almanız gerektiğini unutmayın, böylece daha sonra yerel olarak geri dönebilirsiniz.
Raymond Arteaga

Bu gerçekten soruyu yanıtlıyor, ancak bence bu muhtemelen işlevsel programlama için izlenecek doğru yol değil. İhtiyacımız olan şey, Lodash'tan, transformbelirli koşullar altında kırılabileceğiniz yer, örn. reduceReturnIf(acc, value, returnIf: func)
windmaomao


16

As Kotlin belgelerine diyor kullanarak,return gitmek yoludur. Kotlin ile ilgili iyi olan şey, iç içe geçmiş işlevleriniz varsa, dönüşünüzün nereden geldiğini açıkça yazmak için etiketleri kullanabilirsiniz:

İşlev Kapsam Dönüşü

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

ve Yerel İade (her biri için = devam)

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

Belgeleri inceleyin, gerçekten çok iyi :)


3
Uyarı: dönüş @ lit durmuyorforEach
Jemshit Iskenderov

Bu doğru. Amaçlanıyor. İlk çözüm budur, ancak bir döngü içinde talimatlarınız varsa nereye dönmek / atlamak istediğinizi seçebilirsiniz. İkinci durumda, eğer sadece return kullanırsak, durur ;-)
2018

Geri Dönüş @ lit seviyor Devam ediyor
pqtuan86

10

continue davranışını yazın 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
}

için breaktip davranış Eğer kullanımına sahip for in untilveya for inlistesidir başına NullableveyaNon-Nullable

  1. İçin null listede:

    for (index in 0 until list.size) {
        val item = list[index] // you can use data item now
        if () {
            // your code
            break
        }
    
        // your code
    }
  2. İçin Olmayan null listesinde:

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

2

ForEach () için iç içe döngüler için Break deyimi:

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

Sonuç:

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

Anonim işlevle ifadeye devam edin:

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

Sonuç:

1 2 4 5 

0

belki her biri için değişir

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

hashmap'ler için çalışıyor

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

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

0

Bunun tam olarak ara vermekle ilgili olmayabileceğini biliyorum. forEach , ama o zamandan beri 1.3.70, scanen azından ara sonuçları alabilmemiz için var.

    fun charValue(c: Char) : Int {
      return if (c == '(') 1 else -1
    }

    val all = s.scan(0) { acc, c -> acc + charValue(c) }
    return all.indexOf(-1)

Öyleyse şimdi, ihtiyacınız olmayan tüm sonuçları (benzer map) bir araya getirebilirseniz, ancak bitmesine izin verirseniz, kesintiyi atlayabilir ve son diziyi kontrol edebilirsiniz.

IMHO, temel işlevsel programlama, transformonu bitirmeden ortada atlaması gereken işlemi gerçekten etkilemez. Bu sorunu gerçekten çözmek için, dönüş koşulunu dikkate alan bir işlev oluşturmamız gerekir, örn.

  reduceReturnIf(acc, value, returnIf: func)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.