Kotlinでは、通常のbreak
ループから実行できるように、関数ループとラムダ内でcontinue
またはfor
を実行できません。たとえば、これは機能しません:
(1..5).forEach {
continue@forEach // not allowed, nor break@forEach
}
古いドキュメント があり、これが利用可能であることを述べていますが、実装されていないようです。ラムダ内からcontinue
またはbreak
にしたいときに同じ振る舞いを得る最良の方法は何ですか?
注:この質問は作者によって意図的に書かれ、回答されています(- Self-Answered Questions ) 、そのため、よく聞かれるKotlinのトピックに対する慣用的な回答がSOにあります。また、現在のKotlinでは正確ではないKotlinのアルファ用に書かれたいくつかの本当に古い回答を明確にするため。
同様の機能を提供する、あなたが求めているもの以外の他のオプションがあります。例えば:
filter
:(like a continue
)を使用すると、一部の値の処理を回避できます。
_dataSet.filter { it % 2 == 0 }.forEach {
// do work on even numbers
}
_
takeWhile
:(like a break
)を使用して、機能ループを停止できます。
_dataSet.takeWhile { it < 10 }.forEach {
// do work on numbers as long as they are < 10, otherwise stop
}
_
処理を行い、結果の値をスキップして、一連の異なる条件で停止する、より複雑な、無意味な例は次のとおりです。
_dataSet.asSequence()
.takeWhile { it >= 0 } // a -1 signals end of the dataset (break)
.map { it + 1 } // increment each number
.filterNot { it % 5 == 0 } // skip (continue) numbers divisible by 5
.map { it - 1 } // decrement each number by 1
.filter { it < 100 } // skip (continue) if number is >= 100
.drop(5) // ignore the first 5 numbers
.take(10) // use the next 10 numbers and end
.forEach {
// do work on the final list
}
_
これらの関数の組み合わせは、continue
またはbreak
の必要性を排除する傾向があります。そして、ここには無限のさまざまなオプションがあり、文書化できる以上のものがあります。何ができるかを知るには、 コレクション 、遅延 シーケンス 、および 反復可能 のKotlin標準ライブラリで使用可能なすべての関数を学習するのが最適です。
場合によっては、break
またはcontinue
を必要とする変化状態があり、機能モデルでは実行が難しい場合があります。 fold
およびreduce
のようなより複雑な関数をfilter
およびtakeWhile
関数と組み合わせて使用することで機能させることができますが、時にはそれを理解するのが難しくなります。したがって、その正確な動作が本当に必要な場合は、 ラムダ式からの戻りを使用できます 使用法に応じてcontinue
またはbreak
を模倣します。
continue
を模倣した例を次に示します。
_(1..5).forEach {
if (it == 3) return@forEach // mimic continue@forEach
// ... do something more
}
_
さらに、ネストや混乱の状況がある場合は、より複雑になり、ラベルを使用できます。
_(1..3).forEach outer@ { x ->
(1..3).forEach inner@ { y ->
if (x == 2 && y == 2) return@outer // mimic continue@outer
if (x == 1 && y == 1) return@inner // mimic continue@inner
// ... do something more
}
}
_
break
を実行したい場合は、ループの外側から何かを返す必要があるので、ここでrun()
関数を使用して支援します。
_run breaker@ {
(1..20).forEach { x ->
if (x == 5) return@breaker // mimic break@forEach
// ... do something more
}
}
_
run()
の代わりに、それはlet()
またはapply()
、またはあなたがブレークしたい場所であるforEach
を取り巻く自然なものにすることができます。ただし、forEach
に続く同じブロック内のコードもスキップするため、注意してください。
これらはインライン関数なので、実際にはオーバーヘッドを追加しません。
リターンとジャンプ 匿名関数を含むすべての特殊なケースについては、Kotlinリファレンスドキュメントを参照してください。
これがすべて機能することを証明する単体テストです:
_@Test fun testSo32540947() {
val results = arrayListOf<Pair<Int,Int>>()
(1..3).forEach outer@ { x ->
(1..3).forEach inner@ { y ->
if (x == 2 && y == 2) return@outer // continue @outer
if (x == 1 && y == 1) return@inner // continue @inner
results.add(Pair(x,y))
}
}
assertEquals(listOf(Pair(1,2), Pair(1,3), Pair(2,1), Pair(3,1), Pair(3,2), Pair(3,3)), results)
val results2 = arrayListOf<Int>()
run breaker@ {
(1..20).forEach { x ->
if (x == 5) return@breaker
results2.add(x)
}
}
assertEquals(listOf(1,2,3,4), results2)
}
_
takeWhile stdlib関数をbreakの代わりに使用できます。
例えば、
val array = arrayOf(2, 8, 4, 5, 13, 12, 16)
array.takeWhile { it % 2 == 0 }.forEach { println(it) } // break on odd
array.takeWhile { it % 3 != 0 }.forEach { println(it) } // break on 3 * n