Как бы я получил сумму квадратов двух списков в Scala? - PullRequest
2 голосов
/ 25 октября 2011

Если у меня есть два списка

val first = List(1, 2, 3)
val second = List(4, 5, 6)

Как мне получить следующее?

(1-4)^2 + (2-5)^2 + (3-6)^2

Ответы [ 3 ]

12 голосов
/ 25 октября 2011

почтовый индекс, карта и сумма:

first.view.zip(second).map(t => t._1 - t._2).map(x => x*x).sum
  • zip объединить элементы двух списков в кортеж
  • view используется для ленивого вычисления списка, чтобы не строить структуру между двумя вызовами карты

(изменить, чтобы заменить reduceLeft на sum)


Увидев комментарии, я чувствую, что мне пришлось вернуться и рассказать о взглядах. По сути, представление превращает Traversable в структуру, подобную итератору, поэтому не требуется создавать несколько промежуточных структур при применении таких методов, как map, zip и некоторых других. Члены типа GenIteratableViewLike дают представление о том, какие операции имеют специальную обработку. Поэтому, как правило, если у вас есть набор карт, фильтров, отбрасываний, takeWhile, применяемых последовательно, вы можете использовать представление для увеличения производительности. Основное правило заключается в том, чтобы применять view рано, чтобы минимизировать количество промежуточных List, и при необходимости использовать force в конце, чтобы вернуться к List (или к любой коллекции, которую вы используете). Таким образом, предложение Даниила.

Суть в производительности заключается в том, что на практике, если это важно, вам нужно выполнить проверку реальности. Вот некоторые цифры (чем ниже, тем лучше):

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

Код здесь:

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 }
5 голосов
/ 25 октября 2011

В общем, вы, вероятно, хотите определить функцию, если она сложная, а не применять несколько карт. Вы можете сделать это как вспомогательный метод или анонимно в строке.

def op(i: Int, j: Int) = { val m = i - j; m * m }

(first, second).zipped.map(op).sum

Или

(first, second).zipped.map( (i, j) => { val m = i - j; m * m } ).sum

zipped немного более удобен, чем zip в этих ситуациях, потому что его можно сопоставить с функцией, принимающей 2 аргумента, а не с одним кортежем, для которого необходимо использовать ._1 и ._2 поля.

2 голосов
/ 25 октября 2011

с левой складкой:

(0 /: first.zip (second)) ((a, b) => a + (b._1 - b._2)*(b._1 - b._2))

начиная с почтового индекса.

...