在科特林的`forEach`中`break`和`continue`

时间:2015-09-12 16:11:54

标签: loops foreach lambda kotlin

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

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

10 个答案:

答案 0 :(得分:61)

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

Kotlin Playground处尝试。

答案 1 :(得分:27)

修改
根据Kotlin的old documents - link is broken,应该可以使用注释。但是,它还没有实现。

  

未实现自定义控件结构的中断和继续   但

此功能的issue为3年,所以我想它不会被修复。

原始答案
由于您提供了(Int) -> Unit,因此您无法从中断,因为编译器不知道它是在循环中使用的。

你几乎没有选择:

使用常规for循环:

for (index in 0..times - 1) {
    // your code here
}

如果循环是方法中的最后一个代码
您可以使用return退出该方法(如果不是return value方法,则使用unit。)

使用方法
创建一个自定义重复方法方法,返回Boolean以继续。

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

答案 2 :(得分:25)

您可以使用模仿continuebreak的{​​{3}},具体取决于您的使用情况。

相关问题涉及此问题:return from lambda expression

答案 3 :(得分:10)

作为Kotlin documentation says,使用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")
}

查看文档,真的很好:)

答案 4 :(得分:9)

可以使用以下方法实现突破:

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

答案 5 :(得分:6)

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 until

for (index in 0 until list.size) {
    val item = list[index] // you can use data item now
    if () {
        // your code
        break
    }

    // your code
}

答案 6 :(得分:2)

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 

答案 7 :(得分: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
        }
    }

答案 8 :(得分:0)

  fun part2(ops: List<Int>): Int = ops.asSequence()
    .scan(0) { acc, v -> acc + v }
    .indexOf(-1)

如果您有能力将一个收藏集转换为sequence,通常成本是微不足道的,那么您应该能够利用延期功能。

您可能已经注意到上面的asSequence。这里是为了节省我们遍历整个列表的时间。通过indexOf进行比赛后,比赛将立即停止。答对了!救我们,在这里写一个while

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

答案 9 :(得分:0)

我对此有一个完美的解决方案(:

list.apply{ forEach{ item ->
    if (needsToContinue(item)) return@forEach
    if (needsToBreak(item)) return@apply
}}