Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incrementing Variable in a foldLeft

I have Scala code like this

var i = 1
for(e <- array) {
    acc += e * i
    i += 1
}

I need to multiply the first element in the array by 1, the next by 2, the next by 3 and so on adding it all into an accumulator. I feel that there is a better way of doing this in Scala, maybe even with folding?

like image 248
chrissphinx Avatar asked Mar 05 '13 15:03

chrissphinx


4 Answers

"Better" depends on what your goals are. Short and clear? Probably

{ for (i <- array.indices; e = array(i)) yield (i+1)*e }.sum

or

array.indices.map(i => (i+1)*array(i)).sum

(or slightly faster, since you create the intermediates as you go:

array.indices.iterator.map(i => (i+1)*array(i)).sum

).

You should usually be short and clear.

Fast? Then you'll need to go old-school:

var i = 0
var acc = 0
while (i < array.length) {
  acc += (i+1)*array(i)
  i += 1
}

or use recursion

def sum(a: Array[Int], i: Int = 0, acc: Int = 0): Int =
  if (i >= a.length) acc else sum(a, i+1, (i+1)*a(i) + acc)
sum(array)
like image 87
Rex Kerr Avatar answered Nov 07 '22 13:11

Rex Kerr


I prefer zipWithIndex which is simpler to read:

array.zipWithIndex.map { case (e, i) => e * (i + 1) }.sum
like image 31
maxmc Avatar answered Nov 07 '22 12:11

maxmc


val x = List(1,1,1,1,1,1)
(((0,1) /: x){case ((acc, mult), l) => (acc + (l * mult), mult + 1) })._1

In other words, starting with an accumulator of 0 and a multiplier of 1, fold each element of the list in, changing the accumulator to acc + (l * mult) and incrementing the multiplier by 1. We get the final multiplier out at the end as well, so we call ._1 to just get the accumulator.

Edit: As @RexKerr points in his answer below (and the comment), if performance is a major concern then you're better off using an explicit recursive method.

like image 5
Impredicative Avatar answered Nov 07 '22 13:11

Impredicative


I am not sure if what I suggest is a better way to do it or not, as it is more functional (== it will perform slower):

(0 /: (array zipWithIndex)) {(a, i) => (i._1 * (i._2 + 1)) + a}

This does perform foldLeft on an array which is produced by zipWithIndex method in http://www.scala-lang.org/api/current/index.html#scala.Array

zipWithIndex simply zips the elements of a collection with their indices.

like image 2
Amanj Sherwany Avatar answered Nov 07 '22 13:11

Amanj Sherwany