Kotlin 中 `forEach` 中的 `break` 和 `continue`
Posted
技术标签:
【中文标题】Kotlin 中 `forEach` 中的 `break` 和 `continue`【英文标题】:`break` and `continue` in `forEach` in Kotlin 【发布时间】:2015-12-09 01:07:18 【问题描述】:Kotlin 有非常好的迭代函数,例如 forEach
或 repeat
,但我无法使 break
和 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)
【问题讨论】:
在当前的 Kotlin 中,您确实可以模仿这一点(在等待continue@label
和 break@label
功能时),请参阅相关问题:***.com/questions/34642868/…
这个问题可以用来澄清您是否只询问 break
和 continue
是否存在功能循环,或者您是否正在寻找做同样事情的替代答案。前者似乎是这样,因为你拒绝了后者。
似乎它们是在 kotlin 1.3 中添加的
@TigranBabajanyan 哇!有链接吗?
@voddan,不,我刚刚试过了
【参考方案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,带有continue
和 break
示例: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) -> 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,上面写着“估计 - 无估计”。break
和 continue
只能在循环中工作。 forEach
、repeat
和所有其他方法就是这样:方法而不是循环。 Yoav 提出了一些替代方案,但 break
和 continue
只是不适用于方法。
@YoavSternberg 太棒了!我一直在寻找这种旧文档的平静!所以这个功能还没有实现,留给未来的版本。如果您想创建一个单独的答案,我会标记它
在当前的 Kotlin 中,您确实可以模仿这一点(在等待 continue@label
和 break@label
功能时),请参阅相关问题:***.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,它模仿continue
或break
,具体取决于您的使用情况。
这在相关问题中有所涉及: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】:
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 until
或for in
,列表为Nullable
或Non-Nullable
对于Nullable列表:
for (index in 0 until list.size)
val item = list[index] // you can use data item now
if ()
// your code
break
// your code
对于 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
【讨论】:
以上是关于Kotlin 中 `forEach` 中的 `break` 和 `continue`的主要内容,如果未能解决你的问题,请参考以下文章