Как суммировать поля элементов коллекции, не отображая их сначала (например, foldLeft/reduceLeft)?
Рассмотрим этот класс:
case class Person(val firstName: String, val lastName: String, age: Int)
val persons = Person("Jane", "Doe", 42) :: Person("John", "Doe", 45) ::
Person("Joe", "Doe", 43) :: Person("Doug", "Don", 65) ::
Person("Darius", "Don", 24) :: Person("Dora", "Don", 20) ::
Person("Dane", "Dons", 29) :: Nil
Чтобы получить сумму возраста всех лиц, я могу написать код вроде:
persons.foldLeft(0)(_ + _.age)
Но если я хочу использовать sum
, мне нужно сначала сопоставить значение, и код выглядит следующим образом:
persons.map(_.age).sum
Как я могу использовать метод sum
, не создавая промежуточную коллекцию?
(Я знаю, что такая "оптимизация", скорее всего, не имеет реальной разницы в производительности, когда не выполняется в замкнутом цикле, и я также знаю о ленивых представлениях и т.д.)
Возможно ли иметь код типа
persons.sum(_.age)
что делает foldLeft
/reduceLeft
?
Ответы
Ответ 1
Метод sum
в библиотеке работает не так, но вы можете написать свой собственный, который:
def mySum[T, Res](f: T => Res, seq: TraversableOnce[T])(implicit num: Numeric[Res]) =
seq.foldLeft(num.zero)((acc, b) => num.plus(acc, f(b)))
Вы также можете добавить неявное преобразование, чтобы вы могли называть его как seq.sum(f)
вместо mySum(f, seq)
(для предотвращения конфликтов может потребоваться другое имя, чем sum
):
case class SumTraversableOnce[T](val seq: TraversableOnce[T]) {
def sum[Res](f: T => Res)(implicit num: Numeric[Res]) = mySum(f, seq)(num)
}
implicit def toSumTraversableOnce[T](seq: TraversableOnce[T]) =
SumTraversableOnce(seq)
или, поскольку Scala 2.10,
implicit class SumTraversableOnce[T](val seq: TraversableOnce[T]) {
def sum[Res](f: T => Res)(implicit num: Numeric[Res]) = mySum(f, seq)(num)
}
Ответ 2
Вы ответили сами. Просто используйте view
:
persons.view.map(_.age).sum
Чтобы убедить себя, исследуя рабочий процесс:
persons.view.map { p =>
println("invoking age")
p.age
}.map { x =>
println("modifing age")
x + 0
}.sum
Vs:
persons.map { p =>
println("invoking age")
p.age
}.map { x =>
println("modifing age")
x + 0
}.sum