Kotlin 中 `forEach` 中的 `break` 和 `continue`
`break` and `continue` in `forEach` in Kotlin
Kotlin 有非常好的迭代函数,比如 forEach
或 repeat
,但我无法让 break
和 continue
运算符与它们一起工作(两者都是本地和非本地):
repeat(5) {
break
}
(1..5).forEach {
continue@forEach
}
目标是使用尽可能接近的函数式语法模仿通常的循环。这在一些旧版本的 Kotlin 中绝对是可能的,但我很难重现语法。
问题可能是标签 (M12) 的错误,但我认为第一个示例应该可以正常工作。
在我看来,我在某处读到过一个特殊的 trick/annotation,但我找不到关于该主题的任何参考资料。可能如下所示:
public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
for (index in 0..times - 1) {
body(index)
}
}
编辑:
根据 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
)。
使用方法
创建一个自定义重复方法方法,returns Boolean
用于继续。
public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
for (index in 0 until times) {
if (!body(index)) break
}
}
您可以使用 return from lambda expression,它模仿 continue
或 break
,具体取决于您的用法。
这在相关问题中已涵盖:
这将打印 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)
}
}
这将打印 1 到 4,并在到达 5 时中断。
fun main(args: Array<String>) {
val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
run breaking@ {
nums.forEach {
if (it == 5) return@breaking
println(it)
}
}
}
与 Kotlin documentation says 一样,使用 return
是可行的方法。 kotlin 的好处是,如果你有嵌套函数,你可以使用标签明确地写出你的 return 来自哪里:
函数范围Return
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")
}
和
本地Return(它不会停止通过 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")
}
看看文档,真的很好:)
中断可以使用:
//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")
}
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
对于可空列表:
for (index in 0 until list.size) {
val item = list[index] // you can use data item now
if () {
// your code
break
}
// your code
}
对于不可为空列表:
for (item in list) { // data item will available right away
if () {
// your code
break
}
// your code
}
嵌套循环的中断语句 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
也许将 forEach 更改为
for(it in myList){
if(condition){
doSomething()
}else{
break //or continue
}
}
它适用于 hashmaps
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
,通常成本是微不足道的,那么您应该能够利用延迟功能。
You might already notice asSequence
in the above. It's here for saving us going over the entire list. Right after we have a match via indexOf
, it'll stop. Bingo! Saving us write a while
here.
如 https://medium.com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1
的第 2 部分
我有完美的解决方案 (:
list.apply{ forEach{ item ->
if (willContinue(item)) return@forEach
if (willBreak(item)) return@apply
}}
Kotlin 有非常好的迭代函数,比如 forEach
或 repeat
,但我无法让 break
和 continue
运算符与它们一起工作(两者都是本地和非本地):
repeat(5) {
break
}
(1..5).forEach {
continue@forEach
}
目标是使用尽可能接近的函数式语法模仿通常的循环。这在一些旧版本的 Kotlin 中绝对是可能的,但我很难重现语法。
问题可能是标签 (M12) 的错误,但我认为第一个示例应该可以正常工作。
在我看来,我在某处读到过一个特殊的 trick/annotation,但我找不到关于该主题的任何参考资料。可能如下所示:
public inline fun repeat(times: Int, @loop body: (Int) -> Unit) {
for (index in 0..times - 1) {
body(index)
}
}
编辑:
根据 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
)。
使用方法
创建一个自定义重复方法方法,returns Boolean
用于继续。
public inline fun repeatUntil(times: Int, body: (Int) -> Boolean) {
for (index in 0 until times) {
if (!body(index)) break
}
}
您可以使用 return from lambda expression,它模仿 continue
或 break
,具体取决于您的用法。
这在相关问题中已涵盖:
这将打印 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)
}
}
这将打印 1 到 4,并在到达 5 时中断。
fun main(args: Array<String>) {
val nums = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
run breaking@ {
nums.forEach {
if (it == 5) return@breaking
println(it)
}
}
}
与 Kotlin documentation says 一样,使用 return
是可行的方法。 kotlin 的好处是,如果你有嵌套函数,你可以使用标签明确地写出你的 return 来自哪里:
函数范围Return
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")
}
和 本地Return(它不会停止通过 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")
}
看看文档,真的很好:)
中断可以使用:
//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")
}
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
对于可空列表:
for (index in 0 until list.size) { val item = list[index] // you can use data item now if () { // your code break } // your code }
对于不可为空列表:
for (item in list) { // data item will available right away if () { // your code break } // your code }
嵌套循环的中断语句 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
也许将 forEach 更改为
for(it in myList){
if(condition){
doSomething()
}else{
break //or continue
}
}
它适用于 hashmaps
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
,通常成本是微不足道的,那么您应该能够利用延迟功能。
You might already notice
asSequence
in the above. It's here for saving us going over the entire list. Right after we have a match viaindexOf
, it'll stop. Bingo! Saving us write awhile
here.
如 https://medium.com/@windmaomao/kotlin-day-1-up-and-down-38885a5fc2b1
的第 2 部分我有完美的解决方案 (:
list.apply{ forEach{ item ->
if (willContinue(item)) return@forEach
if (willBreak(item)) return@apply
}}