在 Scala 中使用索引进行高效迭代

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6833501/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 03:16:10  来源:igfitidea点击:

Efficient iteration with index in Scala

scalaiteration

提问by snappy

Since Scala does not have old Java style forloops with index,

由于 Scala 没有for带有索引的旧 Java 样式循环,

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

How can we iterate efficiently, and without using var's?

我们如何在不使用var's 的情况下高效迭代?

You could do this

你可以这样做

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

but the list is traversed twice - not very efficient.

但该列表被遍历了两次 - 效率不高。

回答by Didier Dupont

Much worse than traversing twice, it creates an intermediary array of pairs. You can use view. When you do collection.view, you can think of subsequent calls as acting lazily, during the iteration. If you want to get back a proper fully realized collection, you call forceat the end. Here that would be useless and costly. So change your code to

比遍历两次更糟糕的是,它创建了一个中间数组对。您可以使用view. 当您这样做时collection.view,您可以将后续调用视为迭代期间的惰性操作。如果你想找回一个正确的完全实现的集合,你force在最后调用。在这里,这将是无用的和昂贵的。因此,将您的代码更改为

for((x,i) <- xs.view.zipWithIndex) println("String #" + i + " is " + x)

回答by Kipton Barros

It has been mentioned that Scala doeshave syntax for forloops:

有人提到 Scala确实for循环语法:

for (i <- 0 until xs.length) ...

or simply

或者干脆

for (i <- xs.indices) ...

However, you also asked for efficiency. It turns out that the Scala forsyntax is actually syntactic sugar for higher order methods such as map, foreach, etc. As such, in some cases these loops can be inefficient, e.g. How to optimize for-comprehensions and loops in Scala?

但是,您也要求效率。事实证明,Scala的for语法实际上是更高阶的方法,如语法糖mapforeach等。因此,在某些情况下,这些循环可以是低效的,例如如何优化,内涵和循环在Scala呢?

(The good news is that the Scala team is working on improving this. Here's the issue in the bug tracker: https://issues.scala-lang.org/browse/SI-4633)

(好消息是 Scala 团队正在努力改进这一点。这是错误跟踪器中的问题:https: //issues.scala-lang.org/browse/SI-4633

For utmost efficiency, one can use a whileloop or, if you insist on removing uses of var, tail recursion:

为了获得最高效率,可以使用while循环,或者,如果您坚持删除var尾递归的使用,则:

import scala.annotation.tailrec

@tailrec def printArray(i: Int, xs: Array[String]) {
  if (i < xs.length) {
    println("String #" + i + " is " + xs(i))
    printArray(i+1, xs)
  }
}
printArray(0, Array("first", "second", "third"))

Note that the optional@tailrecannotation is useful for ensuring that the method is actually tail recursive. The Scala compiler translates tail-recursive calls into the byte code equivalent of while loops.

请注意,可选@tailrec注释对于确保该方法实际上是尾递归很有用。Scala 编译器将尾递归调用转换为等效于 while 循环的字节码。

回答by missingfaktor

One more way:

还有一种方法:

scala> val xs = Array("first", "second", "third")
xs: Array[java.lang.String] = Array(first, second, third)

scala> for (i <- xs.indices)
     |   println(i + ": " + xs(i))
0: first
1: second
2: third

回答by om-nom-nom

Actually, scala has old Java-style loops with index:

实际上,scala 有带有索引的旧 Java 风格循环:

scala> val xs = Array("first","second","third")
xs: Array[java.lang.String] = Array(first, second, third)

scala> for (i <- 0 until xs.length)
     | println("String # " + i + " is "+ xs(i))

String # 0 is first
String # 1 is second
String # 2 is third

Where 0 until xs.lengthor 0.until(xs.length)is a RichIntmethod which returns Rangesuitable for looping.

其中0 until xs.length0.until(xs.length)RichInt返回Range适合循环的方法。

Also, you can try loop with to:

此外,您可以尝试使用循环to

scala> for (i <- 0 to xs.length-1)
     | println("String # " + i + " is "+ xs(i))
String # 0 is first
String # 1 is second
String # 2 is third

回答by Matthew Saltz

How about this?

这个怎么样?

val a = Array("One", "Two", "Three")
a.foldLeft(0) ((i, x) => {println(i + ": " + x); i + 1;} )

Output:

输出:

0: One
1: Two
2: Three

回答by Prakhyat

Looping in scala is pretty simple. Create any array of your choice for ex.

在 Scala 中循环非常简单。为 ex 创建您选择的任何数组。

val myArray = new Array[String](3)
myArray(0)="0";
myArray(1)="1";
myArray(2)="2";

Types of loops,

循环类型,

for(data <- myArray)println(data)

for (i <- 0 until myArray.size)
println(i + ": " + myArray(i))

回答by herman

Indeed, calling zipWithIndexon a collection will traverse it and also create a new collection for the pairs. To avoid this, you can just call zipWithIndexon the iterator for the collection. This will just return a new iterator that keeps track of the index while iterating, so without creating an extra collection or additional traversing.

事实上,调用zipWithIndex一个集合将遍历它并为这些对创建一个新集合。为了避免这种情况,您可以调用zipWithIndex集合的迭代器。这将只返回一个在迭代时跟踪索引的新迭代器,因此无需创建额外的集合或额外的遍历。

This is how scala.collection.Iterator.zipWithIndexis currently implemented in 2.10.3:

这是scala.collection.Iterator.zipWithIndex目前在 2.10.3 中的实现方式:

  def zipWithIndex: Iterator[(A, Int)] = new AbstractIterator[(A, Int)] {
    var idx = 0
    def hasNext = self.hasNext
    def next = {
      val ret = (self.next, idx)
      idx += 1
      ret
    }
  }

This should even be a bit more efficient than creating a view on the collection.

这甚至应该比在集合上创建视图更有效。

回答by anish

I have the following approaches

我有以下方法

object HelloV2 {

   def main(args: Array[String]) {

     //Efficient iteration with index in Scala

     //Approach #1
     var msg = "";

     for (i <- args.indices)
     {
       msg+=(args(i));
     }
     var msg1="";

     //Approach #2
     for (i <- 0 until args.length) 
     {
       msg1 += (args(i));
     }

     //Approach #3
     var msg3=""
     args.foreach{
       arg =>
        msg3 += (arg)
     }


      println("msg= " + msg);

      println("msg1= " + msg1);

      println("msg3= " + msg3);

   }
}

回答by Alex Cruise

There's nothing in the stdlib that will do it for you without creating tuple garbage, but it's not too hard to write your own. Unfortunately I've never bothered to figure out how to do the proper CanBuildFrom implicit raindance to make such things generic in the type of collection they're applied to, but if it's possible, I'm sure someone will enlighten us. :)

stdlib 中没有任何内容可以在不创建元组垃圾的情况下为您完成,但编写自己的内容并不太难。不幸的是,我从来没有费心去弄清楚如何做适当的 CanBuildFrom 隐式raindance 来使这些东西在它们所应用的集合类型中通用,但如果可能的话,我相信有人会启发我们。:)

def foreachWithIndex[A](as: Traversable[A])(f: (Int,A) => Unit) {
  var i = 0
  for (a <- as) {
    f(i, a)
    i += 1
  }
}

def mapWithIndex[A,B](in: List[A])(f: (Int,A) => B): List[B] = {
  def mapWithIndex0(in: List[A], gotSoFar: List[B], i: Int): List[B] = {
    in match {
      case Nil         => gotSoFar.reverse
      case one :: more => mapWithIndex0(more, f(i, one) :: gotSoFar, i+1)
    }
  }
  mapWithIndex0(in, Nil, 0)
}

// Tests....

@Test
def testForeachWithIndex() {
  var out = List[Int]()
  ScalaUtils.foreachWithIndex(List(1,2,3,4)) { (i, num) =>
    out :+= i * num
  }
  assertEquals(List(0,2,6,12),out)
}

@Test
def testMapWithIndex() {
  val out = ScalaUtils.mapWithIndex(List(4,3,2,1)) { (i, num) =>
    i * num
  }

  assertEquals(List(0,3,4,3),out)
}

回答by user unknown

Some more ways to iterate:

更多的迭代方式:

scala>  xs.foreach (println) 
first
second
third

foreach, and similar, map, which would return something (the results of the function, which is, for println, Unit, so a List of Units)

foreach 和类似的 map,它会返回一些东西(函数的结果,对于 println,Unit,所以是一个 List of Units)

scala> val lens = for (x <- xs) yield (x.length) 
lens: Array[Int] = Array(5, 6, 5)

work with the elements, not the index

处理元素,而不是索引

scala> ("" /: xs) (_ + _) 
res21: java.lang.String = firstsecondthird

folding

折叠式的

for(int i=0, j=0; i+j<100; i+=j*2, j+=i+2) {...}

can be done with recursion:

for(int i=0, j=0; i+j<100; i+=j*2, j+=i+2) {...}

可以用递归来完成:

def ijIter (i: Int = 0, j: Int = 0, carry: Int = 0) : Int =
  if (i + j >= 100) carry else 
    ijIter (i+2*j, j+i+2, carry / 3 + 2 * i - 4 * j + 10) 

The carry-part is just some example, to do something with i and j. It needn't be an Int.

进位部分只是一些例子,用 i 和 j 做一些事情。它不必是一个 Int。

for simpler stuff, closer to usual for-loops:

对于更简单的东西,更接近通常的 for 循环:

scala> (1 until 4)
res43: scala.collection.immutable.Range with scala.collection.immutable.Range.ByOne = Range(1, 2, 3)

scala> (0 to 8 by 2)   
res44: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)

scala> (26 to 13 by -3)
res45: scala.collection.immutable.Range = Range(26, 23, 20, 17, 14)

or without order:

或没有订单:

List (1, 3, 2, 5, 9, 7).foreach (print)