Hoa
Hoa

Reputation: 20438

How would I get the sum of squares of two Lists in Scala?

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

Answers (3)

huynhjl
huynhjl

Reputation: 41646

zip, map and sum:

first.view.zip(second).map(t => t._1 - t._2).map(x => x*x).sum
  • zip combine elements of the two list into a tuple
  • view is used to have the list computed lazily to not build a structure between the two map calls

(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

user unknown
user unknown

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

Luigi Plinge
Luigi Plinge

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

Related Questions