简体   繁体   English

Kotlin 中 `forEach` 中的 `break` 和 `continue`

[英]`break` and `continue` in `forEach` in Kotlin

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): Kotlin 具有非常好的迭代函数,例如forEachrepeat ,但我无法使用它们进行breakcontinue运算符(本地和非本地):

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.在 Kotlin 的某些旧版本中绝对有可能,但我很难重现语法。

The problem might be a bug with labels (M12), but I think that the first example should work anyway.问题可能是标签 (M12) 的错误,但我认为第一个示例无论如何都应该有效。

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.这将打印 1 到 5。 return@forEach作用类似于 Java 中的关键字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)
    }
}

This will print 1 to 10 but skips 5.这将打印 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)
    }
}

Try them at Kotlin Playground .Kotlin Playground尝试它们。

Edit :编辑
According to Kotlin's documentation , it is possible using annotations.根据 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")
}

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.由于您提供了(Int) -> Unit ,您无法摆脱它,因为编译器不知道它在循环中使用。

You have few options:你有几个选择:

Use a regular for loop:使用常规 for 循环:

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).您可以使用return退出该方法(如果不是unit方法,则return value )。

Use a method使用方法
Create a custom repeat method method that returns Boolean for continuing.创建一个自定义的重复方法方法,该方法返回Boolean以继续。

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正如这里的任何人推荐的那样......阅读文档: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".编辑:虽然主要问题是关于 forEach,但重要的是要考虑旧的“for”。 Using Kotlin doesn't mean we need to use forEach all the time.使用 Kotlin 并不意味着我们需要一直使用 forEach。 Using the good old "for" is perfectly ok, and sometimes even more expressive and concise than forEach:使用旧的“for”完全没问题,有时甚至比 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.您可以使用lambda 表达式的 return ,它根据您的使用情况模拟continuebreak

This is covered in the related question: How do I do a "break" or "continue" when in a functional loop within Kotlin?这在相关问题中有所涉及: 在 Kotlin 中的功能循环中,如何进行“中断”或“继续”?

As the Kotlin documentation says , using return is the way to go.正如Kotlin 文档所说,使用return是正确的方法。 Good thing about kotlin is that if you have nested functions, you can use labels to explicity write where your return is from: kotlin 的好处是,如果你有嵌套函数,你可以使用标签来明确地写出你的返回来自哪里:

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)本地返回(它不会停止通过 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 forEachforEach continue输入行为

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对于break类型行为,您必须使用for in untilfor in根据列表为NullableNon-Nullable

  1. 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 }
  2. 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():嵌套循环 forEach() 的 Break 语句:

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也许将 forEach 改为

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.如果您有能力将集合转换为sequence ,通常成本是微不足道的,那么您应该能够利用延迟功能。

You might already notice asSequence in the above.您可能已经注意到上面的asSequence It's here for saving us going over the entire list.它在这里是为了节省我们浏览整个列表。 Right after we have a match via indexOf , it'll stop.在我们通过indexOf匹配之后,它就会停止。 Bingo!答对了! Saving us write a while here.保存我们while这里写while

as in Part 2 of https://medium.com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1https://medium.com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1 的第 2 部分

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.如果条件取决于列表中前一个元素的结果,则可以使用sequencetakeWhile延迟执行深度优先。

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.您最后需要终端toList()来执行序列。

More details: https://kotlinlang.org/docs/sequences.html#sequence更多详情: https://kotlinlang.org/docs/sequences.html#sequence

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM