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


119

Kotlin具有很好的迭代功能,例如forEachrepeat,但是我无法使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
在当前的科特林你确实可以模仿这种(在等待continue@labelbreak@label功能),请参阅相关的问题:stackoverflow.com/questions/34642868/...
杰森米纳德

1
这个问题可以用来澄清您是在询问功能循环的存在breakcontinue功能循环,还是在寻找功能完全相同的替代答案。前者似乎是这种情况,因为您拒绝了后者。
杰森·米纳德

似乎他们补充说,在科特林1.3
狄格兰Babajanyan

@TigranBabajanyan哇!你有链接吗?
voddan

@voddan,不,我只是尝试了它的工作
Tigran Babajanyan

Answers:


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 2015年

1
我不记得了 但这也许是因为我不经常使用break&继续。看到此问题,它说“估计-无法估算”。
Yoav Sternberg

1
break并且continue只能循环使用。forEachrepeat其他所有方法都只是:方法而不是循环。约阿夫提出了一些办法,但breakcontinue只是不彪工作方法。
Kirill Rakhman 2015年

@YoavSternberg太好了!我一直在寻找旧文档的这种宁静!因此该功能尚未实现,留给以后的版本。如果您想创建一个单独的答案,请给我打上标记
-voddan

在当前的科特林你确实可以模仿这种(在等待continue@labelbreak@label功能),请参阅相关的问题:stackoverflow.com/questions/34642868/...
杰森米纳德

104

这将打印1到5。return@forEach行为类似于continueJava中的关键字,这意味着在这种情况下,它仍然执行每个循环,但如果该值大于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中的每个操作都是lambda函数,目前forEach操作没有确切的中断操作。可以在for循环中找到该中断,请参阅:kotlinlang.org/docs/reference/returns.html
s-hunter

这是一个可运行的Kotlin游乐场代码段,其中包含continuebreakpl.kotl.in/_LAvET-wX
ashughes

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”。这里的关键是本地返回给lambda的调用者。请注意,您需要将循环包装在某个范围内,以便稍后可以对其进行本地返回。
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 '18

那是正确的。是故意的 它是第一个解决方案,但是如果您在循环中有说明,则可以选择要返回/跳转到的位置。在第二种情况下,如果我们仅使用return,它将停止;-)
cesards's

致电Return @ lit喜欢继续
pqtuan86

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

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

也许改变每个

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
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.