Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an equivalent to python reduce() function in scala?

I've just started learning Scala and functional programming and I'm trying to convert the following from Python to Scala:

def immutable_iterative_fibonacci(position):

    if (position ==1):
        return [1]

    if (position == 2):
        return [1,1]

    next_series = lambda series, _: series + [series [-1] + series [-2]]
    return reduce(next_series, range(position - 2), [1, 1])

I can't figure out what the equivalent of reduce in Scala is. This is what I currently have. Everything works fine except the last line.

def immutable_fibonacci(position: Int) : ArrayBuffer[Int] = {

    if (position == 1){
          return ArrayBuffer(1)
     }

     if (position == 2){
         return ArrayBuffer(1,1)
     }

     var next_series = (series: ArrayBuffer[Int]) => series :+ ( series( series.size - 1) + series( series.size -2))

     return reduce(next_series, 2 to position, ArrayBuffer(1,1))
}
like image 908
Pythoner Avatar asked Dec 18 '22 13:12

Pythoner


1 Answers

Summary of Python reduce, for reference:

reduce(function, iterable[, initializer])

Traversable

A good type to look at is Traversable, a supertype of ArrayBuffer. You may want to just peruse that API for a while, because there's a lot of useful stuff in there.

Reduce

The equivalent of Python's reduce, when the initializer arg is omitted, is Scala's Traversable[A]#reduceLeft:

reduceLeft[B >: A](op: (B, A) => B): B

The iterable arg from the Python function corresponds to the Traversable instance, and the function arg from the Python function corresponds to op.

Note that there are also methods named reduce, reduceRight, reduceLeftOption, and reduceRightOption, which are similar but slightly different.

Fold

Your example, which does provide an initializer arg, corresponds to Scala's Traversable[A]#foldLeft:

foldLeft[B](z: B)(op: (B, A) => B): B

The initializer arg from the Python function corresponds to the z arg in foldLeft.

Again, note that there are some related methods named fold and foldRight.

Fibonacci

Without changing the algorithm, here's a cleaned-up version of your code:

def fibonacci(position: Int): Seq[Int] =
  position match {
    case 1 => Vector(1)
    case 2 => Vector(1, 1)
    case _ =>
      (2 to position).foldLeft(Vector(1, 1)) { (series, _) =>
        series :+ (series(series.size - 1) + series(series.size - 2))
      }
  }

A few miscellaneous notes:

  • We generally never use the return keyword
  • Pattern matching (what we're doing with the match keyword) is often considered cleaner than an if-else chain
  • Replace var (which allows multiple assignment) with val (which doesn't) wherever possible
  • Don't use a mutable collection (ArrayBuffer) if you don't need to. Vector is a good general-purpose immutable sequence.

And while we're on the topic of collections and the Fibonacci series, for fun you may want to check out the first example in the Stream documentation:

val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #::
  fibs.zip(fibs.tail).map { n => n._1 + n._2 }

fibs.drop(1).take(6).mkString(" ")
// "1 1 2 3 5 8"
like image 194
Chris Martin Avatar answered Dec 21 '22 03:12

Chris Martin