什么是用于求和对象列表的Scala语法?

时间:2011-09-28 04:24:21

标签: scala

例如

case class Blah(security: String, price: Double)
val myList = List(Blah("a", 2.0), Blah("b", 4.0))
val sum = myList.sum(_.price) // does not work

获得总和的语法是什么?

3 个答案:

答案 0 :(得分:61)

试试这个:

val sum = myList.map(_.price).sum

或者替代:

val sum = myList.foldLeft(0.0)(_ + _.price)

您似乎正在尝试使用此方法:

def sum [B >: A] (implicit num: Numeric[B]): B

并且编译器无法弄清楚您提供的函数是如何Numeric的实例,因为它不是。

答案 1 :(得分:16)

Scalaz在名称foldMap下使用此方法。签名是:

def M[A].foldMap[B](f: A => B)(implicit f: Foldable[M], m: Monoid[B]): B

用法:

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此处不一定是数字类型。它可以是任何幺半群。例如:

scala> myList.foldMap(_.security)
res12: String = ab

答案 2 :(得分:5)

作为missingfaktor的Scalaz示例的替代方案,如果你真的想要总结一个对象的列表(而不是将它们中的每一个映射到一个数字,然后将这些数字相加),scalaz支持这个好。

这取决于为其定义了Monoid实例的相关课程(实际上这意味着它必须定义ZeroSemigroup。幺半群可以被认为是核心scala的Numeric特征的较弱泛化,特别是用于求和;毕竟,如果你可以定义一个零元素和添加/组合两个元素的方法,那么你就拥有了获得多个对象之和所需的一切。

Scalaz'的逻辑与手动求和的方式完全相同 - list.foldLeft(0) { _ + _ } - 除了Zero提供初始零元素,Semigroup提供实现+(称为append)。

它可能看起来像这样:

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

在这种情况下,mySum实际上是Blah的实例等于Blah("ab", 6.0),而不仅仅是一个Double。

好的,对于这个特殊的例子,你并没有真正获得那么多,因为获得安全名称的“总和”并不是很有用。但对于其他类(例如,如果您有数量和价格,或多个相关属性),这可能非常有用。从根本上说,如果你可以定义一些将类的两个实例添加到一起的方法,那么你可以告诉scalaz(通过定义Semigroup);如果你也可以定义零元素,你可以使用该定义轻松地对你的类的集合求和。