我如何做一个"休息"或者"继续"什么时候在Kotlin的功能循环中?

时间:2016-01-06 21:13:46

标签: loops functional-programming break kotlin continue

在Kotlin中,我无法在函数循环和我的lambda中执行breakcontinue - 就像我可以从正常的for循环中执行。例如,这不起作用:

(1..5).forEach {
    continue@forEach  // not allowed, nor break@forEach
}

old documentation提到这个可用,但它似乎从未实现过。当我想在lambda中continuebreak时,获得相同行为的最佳方法是什么?

注意: 此问题是由作者(Self-Answered Questions)故意编写和回答的,因此常见问题的Kotlin主题的惯用答案存在于SO中。还要澄清为Kotlin的alphas写的一些非常古老的答案,这些答案对于当前的Kotlin来说是不准确的。

4 个答案:

答案 0 :(得分:88)

除了您要求提供类似功能之外,还有其他选项。例如:

您可以使用filter避免处理某些值:(,如continue

dataSet.filter { it % 2 == 0 }.forEach {
    // do work on even numbers
}

您可以使用takeWhile停止功能循环:(,如break

dataSet.takeWhile { it < 10 }.forEach {
    // do work on numbers as long as they are < 10, otherwise stop
}

一个更复杂的,虽然荒谬的例子,你想要做一些处理,跳过一些结果值,然后停在一组不同的条件,将是:

dataSet.asSequence()
       .takeWhile { it >=  0 }    // a -1 signals end of the dataset (break)
       .map { it + 1 }            // increment each number
       .filterNot { it % 5 == 0 } // skip (continue) numbers divisible by 5
       .map { it - 1 }            // decrement each number by 1
       .filter { it < 100 }       // skip (continue) if number is >= 100
       .drop(5)                   // ignore the first 5 numbers
       .take(10)                  // use the next 10 numbers and end
       .forEach {
           // do work on the final list
       }

这些功能的组合往往不需要continuebreak。这里有无穷无尽的不同选择,而且可以记录下来。为了了解可以做什么,最好是学习Kotlin标准库中collections,懒惰sequencesiterable的所有可用功能。

有时候你有一些变异状态需要breakcontinue并且在功能模型中很难做到。您可以使用foldreduce等更复杂的函数与filtertakeWhile函数结合使用,但有时候更难以理解。因此,如果您真的想要这种确切的行为,可以使用return from lambda expression来模仿continuebreak,具体取决于您的使用情况。

这是一个模仿continue的示例:

(1..5).forEach  {
    if (it == 3) return@forEach  // mimic continue@forEach
    // ... do something more
}

当你有嵌套或混乱的情况时,你会变得更复杂并使用标签:

(1..3).forEach outer@ { x ->
    (1..3).forEach inner@ { y ->
        if (x == 2 && y == 2) return@outer // mimic continue@outer
        if (x == 1 && y == 1) return@inner // mimic continue@inner
        // ... do something more
    }
}

如果你想做一个break,你需要一些可以返回的循环,我们将使用run()函数来帮助我们:

run breaker@ {
    (1..20).forEach { x ->
        if (x == 5) return@breaker  // mimic break@forEach
        // ... do something more
    }
}

而不是run()它可以是let()apply(),或者你可以自然地围绕forEach这个你要打破的地方。但是你也会在forEach之后跳过同一个区块内的代码,所以要小心。

这些是内联函数,所以实际上它们并没有真正增加开销。

阅读Returns and Jumps的Kotlin参考文档,了解所有特殊情况,包括匿名函数。

这是一个单元测试,证明这一切都有效:

@Test fun testSo32540947() {
    val results = arrayListOf<Pair<Int,Int>>()
    (1..3).forEach outer@ { x ->
        (1..3).forEach inner@ { y ->
            if (x == 2 && y == 2) return@outer // continue @outer
            if (x == 1 && y == 1) return@inner // continue @inner
            results.add(Pair(x,y))
        }
    }

    assertEquals(listOf(Pair(1,2), Pair(1,3), Pair(2,1), Pair(3,1), Pair(3,2), Pair(3,3)), results)

    val results2 = arrayListOf<Int>()
    run breaker@ {
        (1..20).forEach { x ->
            if (x == 5) return@breaker
            results2.add(x)
        }
    }

    assertEquals(listOf(1,2,3,4), results2)
}

答案 1 :(得分:1)

可以使用

takeWhile stdlib函数代替break。

例如,

val array = arrayOf(2, 8, 4, 5, 13, 12, 16)
array.takeWhile { it % 2 == 0 }.forEach { println(it) } // break on odd
array.takeWhile { it % 3 != 0 }.forEach { println(it) } // break on 3 * n

答案 2 :(得分:0)

带有中断的

forEach可以使用any function进行具体替换:

(1..20).any { x ->
    (x == 5).apply { // break on true
        if (!this) {
            results2.add(x)
        }
    }
}

或者甚至更短:

(1..20).any { x ->
    results2.add(x)
    x == 4 // break on true
}

答案 3 :(得分:-1)

如果需要使用continuebreak,则与常规forEach相比,使用for-loop并不理想

如果您真的希望链接命令并像执行for循环那样执行操作,请使用常规功能链,而不要使用forLoop

例如为

    for(i in 0 until 100 step 3) {
        if (i == 6) continue
        if (i == 60) break
        println(i)
    }

map用作循环,将filterNot用作继续,将asSequence() & first用作中断

    (0 until 100 step 3).asSequence()
            .filterNot { it == 6 }
            .map { println(it); it }
            .first { it == 60 }