繁体   English   中英

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

[英]`break` and `continue` in `forEach` in 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)
    }
}

这将打印 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尝试它们。

编辑
根据 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退出该方法(如果不是unit方法,则return value )。

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

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

可以使用以下方法实现中断:

//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 (it == 3) break//or continue
            print(it)
        }
    }
    print("done with the good old for")
}

您可以使用lambda 表达式的 return ,它根据您的使用情况模拟continuebreak

这在相关问题中有所涉及: 在 Kotlin 中的功能循环中,如何进行“中断”或“继续”?

正如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 = 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")
}

查看文档,它真的很好:)

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

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

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

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

嵌套循环 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 

也许将 forEach 改为

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

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

您可能已经注意到上面的asSequence 它在这里是为了节省我们浏览整个列表。 在我们通过indexOf匹配之后,它就会停止。 答对了! 保存我们while这里写while

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

如果条件取决于列表中前一个元素的结果,则可以使用sequencetakeWhile延迟执行深度优先。

sequenceOf(1, 2, 3, 4, 5).map { i ->
    println("i = ${i}")
    i < 3
}.takeWhile { success ->
    println("success = ${success}")
    success
}.toList()

将打印

i = 1
success = true
i = 2
success = true
i = 3
success = false

您最后需要终端toList()来执行序列。

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

暂无
暂无

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

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