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

`break` and `continue` in `forEach` in Kotlin

Kotlin 有非常好的迭代函数,比如 forEachrepeat,但我无法让 breakcontinue 运算符与它们一起工作(两者都是本地和非本地):

repeat(5) {
    break
}

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

目标是使用尽可能接近的函数式语法模仿通常的循环。这在一些旧版本的 Kotlin 中绝对是可能的,但我很难重现语法。

问题可能是标签 (M12) 的错误,但我认为第一个示例应该可以正常工作。

在我看来,我在某处读到过一个特殊的 trick/annotation,但我找不到关于该主题的任何参考资料。可能如下所示:

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

编辑:
根据 Kotlin 的 documentation,可以使用注释来模拟 continue

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

如果你想模拟一个break,只需添加一个run

fun foo() {
    run lit@ {
        listOf(1, 2, 3, 4, 5).forEach {
            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 退出该方法(如果不是 unit 方法,则可以使用 return value)。

使用方法
创建一个自定义重复方法方法,returns Boolean 用于继续。

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

您可以使用 return from lambda expression,它模仿 continuebreak,具体取决于您的用法。

这在相关问题中已涵盖:

这将打印 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)
    }
}

这将打印 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)
    }
}

这将打印 1 到 4,并在到达 5 时中断。

fun main(args: Array<String>) {
    val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    run breaking@ {
        nums.forEach {
           if (it == 5) return@breaking
           println(it)
        }
    }
}

Link to code snippet from ashuges.

Kotlin documentation says 一样,使用 return 是可行的方法。 kotlin 的好处是,如果你有嵌套函数,你可以使用标签明确地写出你的 return 来自哪里:

函数范围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")
}

本地Return(它不会停止通过 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")
}

看看文档,真的很好:)

中断可以使用:

//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

编辑: 虽然主要问题询问的是 forEach,但重要的是要考虑旧的“for”。使用 Kotlin 并不意味着我们需要一直使用 forEach。使用古老的“for”是完全可以的,有时甚至比 forEach 更具表现力和简洁性:

fun foo() {
    for(x in listOf(1, 2, 3, 4, 5){
        if (x == 3) break //or continue
        print(x)
    }
    print("done with the good old for")
}

continueforEach

中输入行为
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 untilfor in 根据列表是 NullableNon-Nullable

  1. 对于可空列表:

    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 (item in list) { // data item will available right away
        if () {
            // your code
            break
        }
    
        // your code
    }
    

嵌套循环的中断语句 forEach():

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 

也许将 forEach 更改为

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

它适用于 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)

如果您有能力将一个集合变成一个 sequence,通常成本是微不足道的,那么您应该能够利用延迟功能。

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 via indexOf, it'll stop. Bingo! Saving us write a while here.

https://medium.com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1

的第 2 部分

我有完美的解决方案 (:

list.apply{ forEach{ item ->
    if (willContinue(item)) return@forEach
    if (willBreak(item)) return@apply
}}