【问题标题】:`break` and `continue` in `forEach` in KotlinKotlin 中 `forEach` 中的 `break` 和 `continue`
【发布时间】:2015-12-09 01:07:18
【问题描述】:

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

【问题讨论】:

  • 在当前的 Kotlin 中,您确实可以模仿这一点(在等待 continue@labelbreak@label 功能时),请参阅相关问题:stackoverflow.com/questions/34642868/…
  • 这个问题可以用来澄清您是否只询问 breakcontinue 是否存在功能循环,或者您是否正在寻找做同样事情的替代答案。前者似乎是这样,因为你拒绝了后者。
  • 似乎它们是在 kotlin 1.3 中添加的
  • @TigranBabajanyan 哇!有链接吗?
  • @voddan,不,我刚刚试过了

标签: loops foreach lambda kotlin


【解决方案1】:

这将打印 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 试用。

【讨论】:

  • 很好,但这仍然不能解决在满足某些条件时无法过早结束 forEach 的问题。它仍然继续执行循环。
  • @TheFox 是的,它执行每个循环,并且在满足条件时跳过返回之后的任何内容。 forEach 中的每个操作都是一个 lambda 函数,目前 forEach 操作没有确切的 break 操作。 break 在 for 循环中可用,请参阅:kotlinlang.org/docs/reference/returns.html
  • 这是一个可运行的 Kotlin Playground sn-p,带有 continuebreak 示例:pl.kotl.in/_LAvET-wX
【解决方案2】:

编辑
根据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) -&gt; 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
    }
}

【讨论】:

  • 实际上,我的问题是关于使特定语法工作,而不是迭代。你不记得在 Kotlin 的某个里程碑上它是可能的吗?
  • 我不记得了。但也许是因为我不经常使用 break & continue 。请参阅this issue,上面写着“估计 - 无估计”。
  • breakcontinue 只能在循环中工作。 forEachrepeat 和所有其他方法就是这样:方法而不是循环。 Yoav 提出了一些替代方案,但 breakcontinue 只是不适用于方法。
  • @YoavSternberg 太棒了!我一直在寻找这种旧文档的平静!所以这个功能还没有实现,留给未来的版本。如果您想创建一个单独的答案,我会标记它
  • 在当前的 Kotlin 中,您确实可以模仿这一点(在等待 continue@labelbreak@label 功能时),请参阅相关问题:stackoverflow.com/questions/34642868/…
【解决方案3】:

可以使用以下方法实现休息:

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

【讨论】:

  • 不错的解决方案。效果很好。虽然似乎不使用@loop 也能得到相同的预期结果。
  • 其实你可以省略显式标签“@loop”而使用隐式标签“@run”。这里的关键方面是本地返回到 lambda 的调用者。请注意,您需要将循环包装在某个范围内,以便稍后在本地返回。
  • 这确实回答了这个问题,但是我认为这可能不是函数式编程的正确路径。我们需要的是,来自 Lodash,transform,您可以在特定条件下中断,例如。 reduceReturnIf(acc, value, returnIf: func)
【解决方案4】:

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

这在相关问题中有所涉及:How do I do a "break" or "continue" when in a functional loop within Kotlin?

【讨论】:

    【解决方案5】:

    作为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 = 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")
    }
    

    查看文档,真的很好:)

    【讨论】:

    • 警告:return@lit 不会停止 forEach
    • 没错。这是有意的。第一个解决方案,但如果你在循环中有指令,你可以选择你想返回/跳转到哪里。在第二种情况下,如果我们只使用 return 它将停止 ;-)
    • 调用 Return@lit 喜欢继续
    【解决方案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 in untilfor in,列表为NullableNon-Nullable

    1. 对于Nullable列表:

      for (index in 0 until list.size) {
          val item = list[index] // you can use data item now
          if () {
              // your code
              break
          }
      
          // your code
      }
      
    2. 对于 Non-Nullable 列表:

      for (item in list) { // data item will available right away
          if () {
              // your code
              break
          }
      
          // your code
      }
      

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

        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 
        

        【讨论】:

          【解决方案9】:
            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 部分中所述

          【讨论】:

            【解决方案10】:

            也许将 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
                    }
                }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-02
              • 2018-08-28
              • 2012-04-07
              • 2011-05-25
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多