Since Scala does not have old Java style for
loops with index,
// does not work
val xs = Array(\"first\", \"second\", \"third\")
for (i=0; i<
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);
}
}
The proposed solutions suffer from the fact that they either explicitly iterate over a collection or stuff the collection into a function. It is more natural to stick with the usual idioms of Scala and put the index inside the usual map- or foreach-methods. This can be done using memoizing. The resulting code might look like
myIterable map (doIndexed(someFunction))
Here is a way to achieve this purpose. Consider the following utility:
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)
}
}
This is already all you need. You can apply this for instance as follows:
import TraversableUtil._
List('a','b','c').map(doIndexed((i, char) => char + i))
which results in the list
List(97, 99, 101)
This way, you can use the usual Traversable-functions at the expense of wrapping your effective function. Enjoy!
It has been mentioned that Scala does have syntax for for
loops:
for (i <- 0 until xs.length) ...
or simply
for (i <- xs.indices) ...
However, you also asked for efficiency. It turns out that the Scala for
syntax 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?
(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)
For utmost efficiency, one can use a while
loop or, if you insist on removing uses of var
, tail recursion:
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 @tailrec
annotation 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.
Looping in scala is pretty simple. Create any array of your choice for 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))
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)
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:
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.
for simpler stuff, closer to usual for-loops:
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)
A simple and efficient way, inspired from the implementation of transform
in SeqLike.scala
var i = 0
xs foreach { el =>
println("String #" + i + " is " + xs(i))
i += 1
}