简体   繁体   中英

Fast Functional Equivalent Of For Loop Scala

I have the following two code snippets in Scala:

/* Iterative */   
for (i <- max to sum by min) {
  if (sum % i == 0) validBusSize(i, L, 0)
}

/* Functional */
List.range(max, sum + 1, min)
    .filter(sum % _ == 0)
    .map(validBusSize(_, L, 0))

Both these code snippets are part of otherwise identical objects. However, when I run my code on Hackerrank, the object with the iterative snippet takes a maximum of 1.45 seconds, while the functional snippet causes the code to take > 7 seconds, which is a timeout.

I'd like to know if it's possible to rewrite the for loop functionally while retaining the speed. I took a look at the Stream container, but again I'll have to call filter before map , instead of computing each validBusSize sequentially.

Thanks!

Edit:

/* Full Code */
import scala.io.StdIn.readLine

object BusStation {

  def main(args: Array[String]) {

    readLine
    val L = readLine.split(" ").map(_.toInt).toList

    val min = L.min
    val max = L.max
    val sum = L.foldRight(0)(_ + _)

    /* code under consideration */
    for (i <- max to sum by min) {
      if (sum % i == 0) validBusSize(i, L, 0)
    }
  }

  def validBusSize(size: Int, L: List[Int], curr: Int) {

    L match {        
      case Nil if (curr == size) => print(size + " ") 
      case head::tail if (curr < size) => 
        validBusSize(size, tail, curr + head)
      case head::tail if (curr == size) => validBusSize(size, tail, head)
      case head::tail if (curr > size) => return
    }
  }
}

Right now, your best bet for fast functional code is tail-recursive functions:

@annotation.tailrec
def getBusSizes(i: Int, sum: Int, step: Int) {
  if (i <= sum) {
    if (sum % i == 0) validBusSize(i, L, 0)
    getBusSizes(i + step, sum, step)
  }
}

Various other things will be sort of fast-ish, but for something like this where there's mostly simple math, the overhead from the generic interface will be sizable. With a tail-recursive function you'll get a while loop underneath. (You don't need the annotation to make it tail-recursive; that just causes the compilation to fail if it can't. The optimization happens whether the annotation is there or not.)

So apparently the following worked:

Replacing the List.range(max, sum + 1, min) with a Range object, (max to sum by min) . Going to ask another questions about why this works though.

Consider converting the range into a parallel version with keyword par , for instance like this

(max to sum by min).par

This may improve performance especially for large sized ranges with large values on calling validBusSize .

Thus in the proposed for comprehension,

for ( i <- (max to sum by min).par ) {
  if (sum % i == 0) validBusSize(i, L, 0)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM