Kotlin has very nice iterating functions, like forEach
or repeat
, but I am not able to make the break
and continue
operators work with them (both local and non-local):
repeat(5) {
break
}
(1..5).forEach {
continue@forEach
}
The goal is to mimic usual loops with the functional syntax as close as it might be. It was definitely possible in some older versions of Kotlin, but I struggle to reproduce the syntax.
The problem might be a bug with labels (M12), but I think that the first example should work anyway.
It seems to me that I've read somewhere about a special trick/annotation, but I could not find any reference on the subject. Might look like the following:
public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
for (index in 0..times - 1) {
body(index)
}
}
This will print 1 to 5. The return@forEach
acts like the keyword continue
in Java, which means in this case, it still executes every loop but skips to the next iteration if the value is greater than 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)
}
}
This will print 1 to 10 but skips 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)
}
}
Try them at Kotlin Playground .
Edit :
According to Kotlin's documentation , it is possible using annotations.
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")
}
Original Answer :
Since you supply a (Int) -> Unit
, you can't break from it, since the compiler do not know that it is used in a loop.
You have few options:
Use a regular for loop:
for (index in 0 until times) {
// your code here
}
If the loop is the last code in the method
you can use return
to get out of the method (or return value
if it is not unit
method).
Use a method
Create a custom repeat method method that returns Boolean
for continuing.
public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
for (index in 0 until times) {
if (!body(index)) break
}
}
A break can be achieved using:
//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")
}
And a continue can be achieved with:
//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")
}
As anyone here recommends... read the docs :P https://kotlinlang.org/docs/reference/returns.html#return-at-labels
EDIT: While the main question asks about forEach, it's important to consider the the good old "for". Using Kotlin doesn't mean we need to use forEach all the time. Using the good old "for" is perfectly ok, and sometimes even more expressive and concise than forEach:
fun foo() {
for(x in listOf(1, 2, 3, 4, 5){
if (it == 3) break//or continue
print(it)
}
}
print("done with the good old for")
}
You can use return from lambda expression which mimics a continue
or break
depending on your usage.
This is covered in the related question: How do I do a "break" or "continue" when in a functional loop within Kotlin?
As the Kotlin documentation says , using return
is the way to go. Good thing about kotlin is that if you have nested functions, you can use labels to explicity write where your return is from:
Function 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")
}
and Local Return (it doesn't stop going through forEach = continuation)
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")
}
Checkout the documentation, it's really good :)
continue
type behaviour 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
}
for break
type behaviour you have to use for in until
or for in
as per the list is Nullable
or Non-Nullable
For Nullable list:
for (index in 0 until list.size) { val item = list[index] // you can use data item now if () { // your code break } // your code }
For Non-Nullable list:
for (item in list) { // data item will available right away if () { // your code break } // your code }
I have the perfect solution for this (:
list.apply{ forEach{ item ->
if (willContinue(item)) return@forEach
if (willBreak(item)) return@apply
}}
Break statement for nested loops forEach():
listOf("a", "b", "c").forEach find@{ i ->
listOf("b", "d").forEach { j ->
if (i == j) return@find
println("i = $i, j = $j")
}
}
Result:
i = a, j = b
i = a, j = d
i = c, j = b
i = c, j = d
Continue statement with anonymous function:
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return
print("$value ")
})
Result:
1 2 4 5
maybe change forEach to
for(it in myList){
if(condition){
doSomething()
}else{
break //or continue
}
}
it works for hashmaps
for(it in myMap){
val k = it.key
val v = it.value
if(condition){
doSomething()
}else{
break //or continue
}
}
fun part2(ops: List<Int>): Int = ops.asSequence()
.scan(0) { acc, v -> acc + v }
.indexOf(-1)
If you can afford to turn a collection into a sequence
, normally the cost is trivial, then you should be able to take advantage of the deferred feature.
You might already notice
asSequence
in the above. It's here for saving us going over the entire list. Right after we have a match viaindexOf
, it'll stop. Bingo! Saving us write awhile
here.
as in Part 2 of https://medium.com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1
If the condition depends on the outcome of a previous element in the list, you can use sequence
and takeWhile
to execute depth-first lazily.
sequenceOf(1, 2, 3, 4, 5).map { i ->
println("i = ${i}")
i < 3
}.takeWhile { success ->
println("success = ${success}")
success
}.toList()
will print
i = 1
success = true
i = 2
success = true
i = 3
success = false
You need the terminal toList()
in the end to execute the sequence.
More details: https://kotlinlang.org/docs/sequences.html#sequence
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.