【问题标题】:Why was the indexed for loop removed in Kotlin?为什么在 Kotlin 中删除了索引 for 循环?
【发布时间】:2017-07-17 12:36:25
【问题描述】:

我仍然认为使用 for 循环的“传统”方式非常强大,可以完全控制索引。为什么它在 Kotlin 中被移除?

我应该在 kotlin 中使用以下 java 代码做什么

for (int i = 0; i < n - 1; i++) {
   for (int j = i + 1; j < n; j++) {
   ....

【问题讨论】:

  • 可能是因为他们可以提供几种替代方法,而无需您读取正在迭代的集合的索引。如果您需要循环直到任意数字,您始终可以在 for 循环中使用范围或仅在 while 循环中使用。
  • (0..collection.size - 1).forEach 有什么问题?它会为您增加索引,您可以使用 collection[it] = blah 改变元素。
  • 或者你可以使用:for (i in 0 .. (n - 1))
  • 我喜欢传统循环的一点是,我可以在任意布尔条件下结束循环。不知道如何在不使用 while 循环的情况下在 Kotlin 中做到这一点。

标签: for-loop kotlin


【解决方案1】:

答案是:因为他们决定删除它。 你仍然可以使用这个语法:

for (a in 1..10) print("$a ")              // >>> 1 2 3 4 5 6 7 8 9 10

for (a in 10 downTo 1 step 2) print("$a ") // >>> 10 8 6 4 2 

更多信息:Ranges & Loops

【讨论】:

  • 我明白了,但是有没有解释第二个循环语法的文档?之前尝试过学习 kotlin,但是因为文档看起来不太好,所以我受够了。
  • 嗨,OP 问为什么 Kotlin 中删除了传统循环而不是如何
  • @Alex 按照我的回答中的“范围”链接。如果你想学习 Kotlin,我建议阅读“Kotlin in Action”一书。
  • @holi-java 好吧,只有 Kotlin 团队的人才能回答为什么
  • @Alex 我想说的是掌握 java 胜过任何书籍。你只需要阅读kotlinlang.org
【解决方案2】:

如果你想同时获取索引和值,可以使用这个。

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

这很简单。

来源:Kotlin Official Documentation - Control Flow

【讨论】:

    【解决方案3】:

    保留这些索引的另一种有用的方法是使用withIndex() 方法进行迭代(即Iterable)。例如:

    for((i, element) in myIterable.withIndex()){
        //do something with your element and the now known index, i
    }
    

    我注意到这与 this 答案中建议的实现相同,但我认为如果我们使用 Iterables 已经为此类情况提供的内置方法而不是再次实现它们会更好。我们可以说这也类似于forEachIndexed{} lambda:

    myIterable.forEachIndexed{i, element ->
        //do something here as well
    }
    

    【讨论】:

      【解决方案4】:

      我认为他们这样做的原因(“未删除”)是因为他们想让 Kotlin 更具表现力。

      例如,在java中我们可以这样创建一个for循环:

      for(int x = 1; x <= 10; x++) {
         System.out.print(x);
      }
      

      你想要做的只是,你想打印出 从 1 到 10 的值,就是这样。所以 Kotlin 决定把你的话翻译成代码,去掉不必要的冗长。

      for (x in 1..10) print("$x") 
      

      【讨论】:

      • 您的两个 sn-ps 将在 Kotlin 中以 Java 打印不同的值。 Kotlin 示例不正确
      • 实际上,根据要求(打印值从 1 到 10),Java 代码是不正确的,因为它只会打印从 1 到 9。更喜欢 Kotlin 语法的另一个原因跨度>
      【解决方案5】:

      在 Kotlin 中无需使用传统的 for 循环,因为您可以使用 Kotlin lambda 使您的代码更具表现力可读性。例如:

      arrayOf(1, 2, 3).forEachIndexed { index, item -> /**TODO**/}
      

      Kotlin 只支持for-each 循环, for-each 循环接受任何 Iterables/ Arrays/ 类型具有 iterator 运算符。

      IF你想回用for-each循环表达式,你可以写如下代码,你可以看到for-each循环会比lamda多,这是为什么中引入了stream api和功能接口:

      val array  = arrayOf(1, 2, 3)
      
      for(index in array.indices){
        val item = array[index]
        // todo
      }
      

      如果你需要回到传统的for循环你可以使用repeat来代替,例如:

      repeat(5){index-> /**TODO**/}
      

      【讨论】:

        【解决方案6】:

        它没有被“删除”。新语言的设计不是从任何现有语言的功能集开始的;我们从一门没有特征的语言开始,然后开始添加以良好和惯用的方式表达某些行为所必需的特征。到目前为止,我们还没有发现 C 风格的“for”循环是表达它的最好和最惯用的方式。

        【讨论】:

        • C 风格的“for”循环有一个简单一致的语法,可以用于任何事情。使用 Kotlin,您可以将这个简单的概念分成许多不同的概念。这是我不喜欢这种语言的少数几个方面之一。
        • @Vitaly 我会说恰恰相反。 Java 有两种不同的循环语法,而 Kotlin 只有一种,外加一个额外的范围概念,它在循环之外还有其他用途。
        • 简单的、基于计数器的、C 风格的 for 循环无需优化就可以编译成非常高效的机器代码。 Kotlin 风格的循环在幕后似乎相当复杂。什么可以确保在所有平台和所有编译器上这些循环都得到很好的优化?
        • @Endre 看起来作为循环的一部分的范围实际上被编译为优化的字节码(至少对于以 JVM 为目标的 Kotlin 1.5.0),即使 IDE 会声称诸如 @ 之类的运算符987654321@ 或 until 创建一个 IntRange 对象。虽然在调用接受范围的函数时要小心,例如String.substring(IntRange),因为目前它们似乎创建了IntRange 对象,这与调用String.substring(Int, Int) 相比相当浪费。
        • 另外,如果你想使用浮点变量进行迭代呢?祝你好运。我不得不重新发明传统的 for 循环,因为 kotlin 范围和级数不包括浮点类型。我认为在没有真正考虑所有用例的下降替换的情况下删除传统的 for 是 kotlin 团队的过激行为。
        【解决方案7】:

        您可以创建自己的函数来实现“完全控制索引的传统循环”。 或非传统。 例如,混合forEachIndexed()with()count(),索引为 1。

        inline fun <T> Iterable<T>.forWithIndexed1(action: T.(index: Int) -> Unit): Int {
            var index = 0
            for (item in this) item.action(++index)
            return index
        }
        

        【讨论】:

        • 使用扩展功能的好方法。虽然我必须说这个方法已经默认实现在可迭代对象上。详情请见我的answer
        【解决方案8】:

        这没有任何意义,因为有时您希望拥有更多带有自定义步骤的自定义循环,而这种结构会从逻辑中删除它

        var array = ["-m", "/test/app", "-w", "us"];
        
        for (var i = 0; i < array.length; i += 2) {
            console.log("option =" + array[i]);
            console.log("value =" + array[i+1])
        }

        使用当前的 kotlin 循环,我们失去了一些简单性。

        【讨论】:

          【解决方案9】:

          Kotlin 正在尝试强制执行流畅的迭代,它涵盖了绝大多数循环结构。但是,我需要一个具有立即终止状态范围的传统 for 循环(如 java)、在循环体执行之前评估的退出条件和执行后操作(我的用例很复杂:我需要使用动态退出重复基于压缩例程和 2) 的幂的条件。我想出了这些通用功能:

          fun <S> forloop(state: S, condition: (s: S) -> Boolean, postExecution: (s: S) -> Unit, block: (s: S) -> Unit) {
            while (condition(state)) {
              block(state)
              postExecution(state)
            }
          }
          
          data class SingleIndex(var i: Int)
          fun forloop(i: Int, condition: (s: SingleIndex) -> Boolean, postExecution: (s: SingleIndex) -> Unit, block: (s: SingleIndex) -> Unit) {
            forloop(SingleIndex(i), condition, postExecution, block)
          }
          

          例子here:

          fun main() {
              forloop(0, {it.i < 10}, {++it.i}) {
                  println("forloop test: i=${it.i}")
              }
          
              data class State(var i: Int, var j: Int)
              forloop(State(0, 1), {it.i < 10}, {++it.i; it.j += it.i}) {
                  println("forloop test: i=${it.i}, j=${it.j}")
              }
          }
          

          尽管根据语言哲学,您应该只在极少数情况下使用它。

          【讨论】:

            【解决方案10】:
            generateSequence(0) { it + 2 }.takeWhile { it < array.length }.forEach {
                // Do something useful
            }
            

            【讨论】:

            • 如何替换问题中的代码?
            • 其实这就是我一直在寻找的答案。它可以让你做任何 java for 循环会做的事情,我需要经常使用这种东西。我什至会争辩说这是线程中最好的答案(尽管它可以从描述中受益)。
            【解决方案11】:

            它没有被删除。这只是它的不同语法。 相当于Java代码:

            for (int i = 0; i < n - 1; i++) {
               for (int j = i + 1; j < n; j++) {
            

            在 Kotlin 中会这样写:

            val n = 10
            for (i in 0..n-2) {
                for (j in i+1..n-1) {
                    println("I'm low level for loop! I just look more friendly.")
                }
            }
            

            为了代码简洁,Kotlin 使用Ranges 来定义循环的迭代器。

            【讨论】:

            • for (i in 0..n-2) { 值得注意的是,如果使用for(i in 0 until n-1),则不需要操作结束索引
            【解决方案12】:

            为什么? “传统”循环是“索引超出范围”异常的原因,不像 集合项目的循环。

            【讨论】:

            • 嗯,不知何故,我认为它让我对流程有更多的控制权。
            • 不是这个原因。您可以使用带范围的循环轻松获取 IOOB 异常
            • 在可以进行集合迭代时不要使用范围循环。
            • 根据您的回答,我开始更喜欢 Kotlin 推理。 :)
            【解决方案13】:

            对于这种确切情况,可以使用以下双循环:

            for (i in 0 until n) {
                    for (j in (i + 1) .. n) {
            

            【讨论】:

              【解决方案14】:
              for (i in generateSequence(0) { it + 2 }.takeWhile { it < array.length }) {
                  // do something useful
              }
              

              【讨论】:

              • 您好,欢迎来到 SO。请避免发布仅代码的答案。至少,添加对上下文及其作用的解释。
              猜你喜欢
              • 2020-08-25
              • 2022-09-29
              • 2021-05-29
              • 1970-01-01
              • 2017-11-15
              • 2017-04-27
              • 1970-01-01
              • 2018-08-12
              相关资源
              最近更新 更多