I wrote an answer to the first Project Euler question:
Add all the natural numbers below one thousand that are multiples of 3 or 5.
The first thing that came to me was:
(1 until 1000).filter(i => (i % 3 == 0 || i % 5 == 0)).foldLeft(0)(_ + _)
but it’s slow (it takes 125 ms), so I rewrote it, simply thinking of ‘another way’ versus ‘the faster way’
(1 until 1000).foldLeft(0){
(total, x) =>
x match {
case i if (i % 3 == 0 || i % 5 ==0) => i + total // Add
case _ => total //skip
}
}
This is much faster (only 2 ms). Why? I’m guess the second version uses only the Range generator and doesn’t manifest a fully realized collection in any way, doing it all in one pass, both faster and with less memory. Am I right?
Here the code on IdeOne: http://ideone.com/GbKlP
The problem, as others have said, is that
filtercreates a new collection. The alternativewithFilterdoesn’t, but that doesn’t have afoldLeft. Also, using.view,.iteratoror.toStreamwould all avoid creating the new collection in various ways, but they are all slower here than the first method you used, which I thought somewhat strange at first.But, then… See,
1 until 1000is aRange, whose size is actually very small, because it doesn’t store each element. Also,Range‘sforeachis extremely optimized, and is evenspecialized, which is not the case of any of the other collections. SincefoldLeftis implemented as aforeach, as long as you stay with aRangeyou get to enjoy its optimized methods.(_: Range).foreach:(_: Range).view.foreach(_: Range).view.iterator(_: Range).view.iterator.foreachAnd that, of course, doesn’t even count the
filterbetweenviewandfoldLeft: