Reputation: 20438
If I have two lists
val first = List(1, 2, 3)
val second = List(4, 5, 6)
How would I obtain the following?
(1-4)^2 + (2-5)^2 + (3-6)^2
Upvotes: 2
Views: 1845
Reputation: 41646
zip, map and sum:
first.view.zip(second).map(t => t._1 - t._2).map(x => x*x).sum
(edit to replace reduceLeft
by sum
)
After seeing the comments, I feel I had to come back and explain about views. Basically a view turns a Traversable
into an iterator like structure so that multiple intermediate structures don't have to be created when apply methods like map
, zip
and a few others. The type members of GenIteratableViewLike gives a sense of what operations have special processing. So typically if you have a bunch of map, filter, drop, takeWhile applied in sequence, you can use view to gain some performance. The rule of thumb is to apply view
early to minimize how many intermediate List
are created and if necessary use force
at the end to go back to List
(or whatever collection you're using). Thus Daniel's suggestion.
The thing about performance is that in practice if that's important you sort of have to do a reality check. Here are some numbers (lower is better):
no view List(62, 62, 62, 62, 63) sum: 311
view before zip List(32, 31, 15, 16, 31) sum: 125
view after zip List(31, 46, 46, 31, 31) sum: 185
iterator List(16, 16, 16, 16, 15) sum: 79
zipped List(62, 47, 62, 46, 47) sum: 264
Code is here:
import testing.Benchmark
def lots[T](n: Int, f: => T): T = if (n > 0) { f; lots(n - 1, f) } else f
def bench(n: Int, id: String)(block: => Unit) {
val times = (new testing.Benchmark {
def run() = lots(10000, block)
}).runBenchmark(n)
println(id + " " + times + " sum: " + times.sum)
}
val first = List(1, 2, 3)
val second = List(4, 5, 6)
bench(5, "no view") { first.zip(second).map(t => t._1 - t._2).map(x => x*x).sum }
bench(5, "view before zip") { first.view.zip(second).map(t => t._1 - t._2).map(x => x*x).sum }
bench(5, "view after zip") { first.zip(second).view.map(t => t._1 - t._2).map(x => x*x).sum }
bench(5, "iterator") { first.iterator.zip(second.iterator).map(t => t._1 - t._2).map(x => x*x).sum }
bench(5, "zipped") { (first, second).zipped.map((a,b) => a - b).map(x => x*x).sum }
Upvotes: 12
Reputation: 36229
with a left fold:
(0 /: first.zip (second)) ((a, b) => a + (b._1 - b._2)*(b._1 - b._2))
starting with a zip.
Upvotes: 2
Reputation: 51099
In general you probably want to define a function if it's anything complex, rather than applying multiple maps. You can do this as a helper method, or anonymously in-line.
def op(i: Int, j: Int) = { val m = i - j; m * m }
(first, second).zipped.map(op).sum
Or
(first, second).zipped.map( (i, j) => { val m = i - j; m * m } ).sum
zipped
is a bit more convenient than zip
in these situations, because it can be mapped to a function taking 2 arguments, rather than a single tuple on which you have to use the ._1
and ._2
fields.
Upvotes: 5