What is the Scala syntax for summing a List of objects?
For example
case class Blah(security: String, price: Double)
val myList = List(Blah("a", 2.0), Blah("b", 4.0))
val sum = myList.sum(_.price) /开发者_如何学C/ does not work
What is the syntax for obtaining the sum?
Try this:
val sum = myList.map(_.price).sum
Or alternately:
val sum = myList.foldLeft(0.0)(_ + _.price)
You appear to be trying to use this method:
def sum [B >: A] (implicit num: Numeric[B]): B
and the compiler can't figure out how the function you're providing is an instance of Numeric
, because it isn't.
Scalaz has this method under a name foldMap
. The signature is:
def M[A].foldMap[B](f: A => B)(implicit f: Foldable[M], m: Monoid[B]): B
Usage:
scala> case class Blah(security: String, price: Double)
defined class Blah
scala> val myList = List(Blah("a", 2.0), Blah("b", 4.0))
myList: List[Blah] = List(Blah(a,2.0), Blah(b,4.0))
scala> myList.foldMap(_.price)
res11: Double = 6.0
B
here doesn't have to be a numeric type. It can be any monoid. Example:
scala> myList.foldMap(_.security)
res12: String = ab
As an alternative to missingfaktor's Scalaz example, if you really want to sum a list of objects (as opposed to mapping each of them to a number and then summing those numbers), scalaz supports this as well.
This depends on the class in question having an instance of Monoid defined for it (which in practice means that it must have a Zero and a Semigroup defined). The monoid can be considered a weaker generalisation of core scala's Numeric
trait specifically for summing; after all, if you can define a zero element and a way to add/combine two elements, then you have everything you need to get the sum of multiple objects.
Scalaz' logic is exactly the same as the way you'd sum integers manually - list.foldLeft(0) { _ + _ }
- except that the Zero
provides the initial zero element, and the Semigroup
provides the implementation of +
(called append
).
It might look something like this:
import scalaz._
import Scalaz._
// Define Monoid for Blah
object Blah {
implicit def zero4Blah: Zero[Blah] = zero(Blah("", 0))
implicit def semigroup4Blah: Semigroup[Blah] = semigroup { (a, b) =>
// Decide how to combine security names - just append them here
Blah(a.security + b.security, a.price + b.price)
}
}
// Now later in your class
val myList = List(Blah("a", 2.0), Blah("b", 4.0))
val mySum = myList.asMA.sum
In this case mySum
will actually be an instance of Blah equal to Blah("ab", 6.0)
, rather than just being a Double.
OK, for this particular example you don't really gain that much because getting a "sum" of the security names isn't very useful. But for other classes (e.g. if you had a quantity as well as a price, or multiple relevant properties) this can be very useful. Fundamentally it's great that if you can define some way of adding two instances of your class together, you can tell scalaz about it (by defining a Semigroup
); and if you can define a zero element too, you can use that definition to easily sum collections of your class.
精彩评论