Kotlinの「forEach」の「break」と「continue」


120

Kotlinには、forEachまたはのような非常に優れた反復関数repeatがありますが、breakand continue演算子でそれらを操作することはできません(ローカルと非ローカルの両方)。

repeat(5) {
    break
}

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

目標は、関数構文を通常のループにできるだけ近づけることです。一部の古いバージョンのKotlinではそれは間違いなく可能でしたが、構文を再現するのに苦労しています。

問題はラベル(M12)のバグかもしれませんが、最初の例はとにかく動作するはずです。

特別なトリック/注釈についてどこかで読んだようですが、この件に関する参照を見つけることができませんでした。次のようになります。

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

1
現在Kotlinでは、この(待っている間に実際に模倣することができますcontinue@labelし、break@label機能)、関連する質問を参照してください。stackoverflow.com/questions/34642868/...
ジェイソンMinard

1
この質問では、関数ループの存在とbreakそのcontinueループについてのみ尋ねているのか、それともまったく同じことをする別の答えを探しているのかを明確にすることができます。前者は後者を拒否したため、そうであるように思われます。
Jayson Minard、2016年

kotlin 1.3で追加されたようです
Tigran Babajanyan

ありがとう リンクはありますか?
ヴォダン2018年

@voddan、いや、私はそれを試してみました
Tigran Babajanyan '30

回答:


68

編集
Kotlinのドキュメントによると、アノテーションを使用することが可能です。

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

元の答え:を指定した
ので(Int) -> Unit、コンパイラーがループで使用されていることを知らないため、それを解除することはできません。

いくつかのオプションがあります:

通常のforループを使用します。

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

ループがメソッドの最後のコードである
場合、メソッドreturnから抜け出すために使用できます(またはメソッドreturn valueでないunit場合)。

メソッドを使用する続行
するBooleanために戻るカスタムの繰り返しメソッドメソッドを作成します。

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

実際、私の質問は、特定の構文を機能させることについてであり、反復についてではありませんでした。Kotlinのあるマイルストーンでそれが可能だったことを覚えていませんか?
voddan

1
覚えていません。しかし、たぶん、Break&Continueをあまり使わないためかもしれません。この問題を参照すると、「推定-推定なし」と表示されます。
Yoav Sternberg

1
breakそしてcontinue唯一のループで動作します。forEachrepeatおよび他のすべてのメソッドは、ループではなくメソッドです。ヨアフは、いくつかの選択肢を提示したがbreakそしてcontinueちょうど方法のために仕事にメンターされていません。
Kirill Rakhman、2015

@YoavSternbergブリリアント!古いドキュメントのこの平和は私が探していたものです!したがって、この機能はまだ実装されておらず、将来のバージョンに残されています。別の回答を作成する場合は、マークを付けます
voddan

現在Kotlinでは、この(待っている間に実際に模倣することができますcontinue@labelし、break@label機能)、関連する質問を参照してください。stackoverflow.com/questions/34642868/...
ジェイソンMinard

104

これは1から5を出力します。Java return@forEachのキーワードのように機能します。continueつまり、この場合、すべてのループを実行しますが、値が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)
    }
}

これは1から10を出力しますが、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)
    }
}

Kotlin Playgroundでお試しください。


すばらしいですが、これでも、何らかの条件が満たされたときにforEachを途中で終了できないという問題には対処できません。それはまだループを実行し続けます。
フォックス

1
@TheFoxはい、それはすべてのループを実行し、条件が満たされたときに戻り値以降はスキップされます。forEachの各操作はラムダ関数です。現在、forEach操作には厳密な中断操作はありません。ブレークはforループで使用できます。次を参照してください:kotlinlang.org/docs/reference/returns.html
s-hunter

これは、continuebreak例の両方を含む実行可能なKotlin Playgroundスニペットです。pl.kotl.in
wX

34

休憩は以下を使用して達成できます。

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

そして継続は次のもので達成できます:

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

ここの誰もが推奨するように...ドキュメントを読んでください:P https://kotlinlang.org/docs/reference/returns.html#return-at-labels


素晴らしい解決策。非常によく機能します。使用しなくて@loopも同じように望ましい結果が得られます。
Paras Sidhu

実際、明示的なタグ「@loop」を省略して、暗黙的なタグ「@run」を使用できます。ここで重要なのは、ラムダの呼び出し元へのローカルな戻りです。後でローカルに戻ることができるように、ループをスコープ内にラップする必要があることに注意してください。
Raymond Arteaga


17

以下のようKotlinのドキュメント言う使用して、return移動するための方法です。kotlinの良い点は、ネストされた関数がある場合、ラベルを使用して戻り値の場所を明示的に記述できることです。

関数スコープの戻り

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

ローカルリターン(forEach =継続を通過することを止めない

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

ドキュメントをチェックアウトしてください、それは本当に良いです:)


3
警告:return @ litは停止しませんforEach
Jemshit Iskenderov

それは正しいです。それは意図されています。最初の解決策ですが、ループ内に命令がある場合は、どこに戻る/ジャンプするかを選択できます。2番目のケースでは、returnのみを使用すると停止します;-)
12:15に

Return @ litのいい

10

continue タイプの動作 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
}

breakタイプの振る舞いあなたは使用する必要がありますfor in untilか、for inリストのとおりですNullableか、Non-Nullable

  1. 以下のためのNULL可能リスト:

    for (index in 0 until list.size) {
        val item = list[index] // you can use data item now
        if () {
            // your code
            break
        }
    
        // your code
    }
  2. 以下のための非NULL可能リスト:

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

2

ネストされたループforEach()のBreakステートメント:

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

結果:

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

無名関数でステートメントを続ける:

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

結果:

1 2 4 5 

0

おそらくforEachを

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

ハッシュマップで機能します

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

       if(condition){
         doSomething()
       }else{
         break //or continue
        }
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.