Efficient iteration with index in Scala

前端 未结 12 536
后悔当初
后悔当初 2020-12-12 11:57

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<         


        
相关标签:
12条回答
  • 2020-12-12 12:14

    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);
    
       }
    }
    
    0 讨论(0)
  • 2020-12-12 12:18

    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!

    0 讨论(0)
  • 2020-12-12 12:20

    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.

    0 讨论(0)
  • 2020-12-12 12:22

    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))
    
    0 讨论(0)
  • 2020-12-12 12:22

    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) 
    
    0 讨论(0)
  • 2020-12-12 12:26

    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
        }
    
    0 讨论(0)
提交回复
热议问题