Scala中的高效索引迭代

95

由于Scala没有旧的Java风格的带索引的for循环,

// does not work
val xs = Array("first", "second", "third")
for (i=0; i<xs.length; i++) {
  println("String #" + i + " is " + xs(i))
}

我们如何高效地迭代,而且不使用var

你可以这样做

val xs = Array("first", "second", "third")
val indexed = xs zipWithIndex
for (x <- indexed) println("String #" + x._2 + " is " + x._1)

但是这个列表被遍历了两次——并不是很高效。


这些都是很好的回应。我在Java的“for”循环中缺少的是具有多个初始化程序的能力,以及使用除增量/减量之外的更多迭代方式的能力。这是Java比Scala更简洁的一个例子。 - snappy
在编程中,“迭代”不仅可以使用增量/减量。在Scala中,可以使用步长进行迭代,或者在循环头中使用“if”条件进行迭代。或者您正在寻找其他内容? - om-nom-nom
1
/Java/ for(int i=0, j=0; i+j<100; i+=j*2, j+=i+2) {...} 你如何在Scala中用一行代码实现这个功能? - snappy
3
在我看来,将Scala翻译成最自然的方式是使用while循环。据我回忆,几年前曾经有一个争论,是否应该让Scala继承Java的for(;;)循环,而当时决定不采用这种方式是因为其增加的复杂性没有带来足够的好处。 - Kipton Barros
12个回答

2

灵感来自于 SeqLike.scalatransform 的实现方式,这是简单而高效的方法。

    var i = 0
    xs foreach { el =>
      println("String #" + i + " is " + xs(i))
      i += 1
    }

0

提出的解决方案存在一个问题,即它们要么明确地迭代集合,要么将集合塞入函数中。更自然的做法是坚持Scala的惯用语法,并将索引放在通常的map-或foreach方法内部。这可以通过记忆化来实现。最终的代码可能如下所示:

myIterable map (doIndexed(someFunction))

以下是一种实现此目的的方法。请考虑以下实用工具:

object TraversableUtil {
    class IndexMemoizingFunction[A, B](f: (Int, A) => B) extends Function1[A, B] {
        private var index = 0
        override def apply(a: A): B = {
            val ret = f(index, a)
            index += 1
            ret
        }
    }

    def doIndexed[A, B](f: (Int, A) => B): A => B = {
        new IndexMemoizingFunction(f)
    }
}

这已经是你所需要的全部了。例如,你可以按照以下方式应用它:

import TraversableUtil._
List('a','b','c').map(doIndexed((i, char) => char + i))

这将导致列表

List(97, 99, 101)

这样,您可以在包装有效函数的代价下使用通常的Traversable函数。享受吧!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接