scala 如何在不Map集合元素的情况下对集合元素的字段求和(如foldLeft/reduceLeft)?

hec6srdp  于 2023-01-13  发布在  Scala
关注(0)|答案(3)|浏览(135)

请看这个类:

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,我需要先Map值,代码如下所示:

persons.map(_.age).sum

如何使用sum方法 * 而不 * 创建一些中间集合?
(我知道这样的“优化”在不以紧密循环运行时很可能不会有任何真实的的性能差异,我还知道惰性视图等等。)
有没有可能有这样的代码

persons.sum(_.age)

foldLeft/reduceLeft做的事情?

eni9jsuy

eni9jsuy1#

您的答案是您自己。只需使用view

persons.view.map(_.age).sum

要通过检查工作流程来说服自己:

persons.view.map { p =>
  println("invoking age")
  p.age
}.map { x =>
  println("modifing age")
  x + 0
}.sum

对比:

persons.map { p =>
  println("invoking age")
  p.age
}.map { x =>
  println("modifing age")
  x + 0
}.sum
aiazj4mn

aiazj4mn2#

库中的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) 
}
qltillow

qltillow3#

在Scala 3中,您可以创建以下扩展:

extension [T](iterable: Iterable[T])
  def sumBy[U](f: T => U)(using n: Numeric[U]): U = iterable.foldLeft(n.zero)((acc, elem) => n.plus(acc, f(elem)))

相关问题