7

I frequently need to sum the transformation of a list of numbers in Scala. One way to do this of course is:

list.map(transform(_)).sum

However, this creates memory when creating memory is not required. An alternative is to fold the list.

list.foldLeft(0.0) { (total, x) => total + f(x) }

I find the first expression far easier to write than the second expression. Is there a method I can use that has the ease of the first with the efficiency of the second? Or am I better off writing my own implicit method?

list.mapSum(transform(_))
schmmd
  • 18,650
  • 16
  • 58
  • 102
  • 1
    Only tangentially related, but be careful if summing transformations over **sets**, e.g.: `Set(1,2,3).map(_ % 2).sum` is `1`, not `2`. I recently got bitten by something like `setOfPlayers.map(_.salary).sum`... – Nicolas Payette Oct 11 '11 at 21:15

2 Answers2

12

You can use a view to make your transformer methods (map, filter...) lazy. See here for more information.

So for example in your case of a method called transform, you would write

list.view.map(transform).sum

(note you can optionally omit the (_))

Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180
  • I had remembered this, but some faulty testing convinced me this was making things slower. Today, after some more playing around, `view` seems great! – schmmd Oct 11 '11 at 18:09
  • @schmmd in some trivial cases implementing a `view` will make things slower, which might be why it isn't automagically applied by default. I think it depends on factors like the proportion of the elements you're using / filtering out. Also speed != memory. So the best thing is probably to try it and benchmark, if speed is critical. – Luigi Plinge Oct 11 '11 at 18:15
  • I should just use an iterator in this example. – schmmd Mar 27 '12 at 22:08
  • @schmmd That's certainly an option where you only need to deal with the whole list, or the first n elements. Views are more flexible insofar as you can access elements randomly as many times as you want. Iterators come from imperative programming and have mutable state, so are generally best avoided in functional code. – Luigi Plinge Mar 27 '12 at 22:56
  • Well understood, but views are considered extremely problematic in scala to the point that many people want to remove them, so I avoid them. – schmmd Mar 29 '12 at 16:03
5

This operation is called foldMap, and you can find it in Scalaz.

list foldMap transform
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • 3
    +1 when I start using scalaz, my future replacement will really hate me. – schmmd Oct 11 '11 at 18:10
  • As given, this expression doesn't sum elements, does it? – Luigi Plinge Oct 11 '11 at 18:18
  • 1
    To elaborate further, `foldMap` has the following type signature: `def foldMap[A, M: Monoid](t: F[A], f: A => M): M`, so your transform has to return something that is a Monoid, and hence, has a "zero" and a `|+|` operation, which `foldMap` will use to accumulate a result. – Nicolas Payette Oct 11 '11 at 21:23
  • 1
    @Zwirb in that signature there are two parameters, but I only see one argument in the answer. What is `F[A]`? – Luigi Plinge Oct 12 '11 at 02:34
  • 1
    @LuigiPlinge: you're right! I quoted the method def from the `Foldable` trait instead of the one in the `MA` trait, which is the one that is actually used here (and ends up calling the method in `Foldable`): `def foldMap[B](f: A => B)(implicit r: Foldable[M], m: Monoid[B]): B = r.foldMap(value, f)`, where `value` is your original list that got pimped into something with the `MA` trait. It's also confusing to me, btw: I'm barely starting to find my way around scalaz... – Nicolas Payette Oct 12 '11 at 04:51