Как суммировать поля элементов коллекции, не отображая их сначала (например, foldLeft / reduLeft)? - PullRequest
14 голосов
/ 21 февраля 2011

Рассмотрим этот класс:

 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 делает?

Ответы [ 2 ]

12 голосов
/ 21 февраля 2011

Вы ответили сами.Просто используйте 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
11 голосов
/ 21 февраля 2011

Метод 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) 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...