Каков синтаксис Scala для суммирования списка объектов? - PullRequest
21 голосов
/ 28 сентября 2011

Например

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 ]

61 голосов
/ 28 сентября 2011

Попробуйте:

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

Или поочередно:

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

Похоже, вы пытаетесь использовать этот метод:

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

и компилятор можетне могу понять, как предоставляемая вами функция является экземпляром Numeric, потому что это не так.

16 голосов
/ 28 сентября 2011

У Скалаза есть этот метод под именем 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
5 голосов
/ 28 сентября 2011

В качестве альтернативы примеру Scalaz отсутствующего фактора, если вы действительно хотите суммировать список объектов (в отличие от сопоставления каждого из них с числом и последующего суммирования этих чисел), скаляр поддерживает это также .

Это зависит от рассматриваемого класса, для которого определен экземпляр Моноид (что на практике означает, что он должен иметь Ноль и Полугруппа определены). Моноид можно рассматривать как более слабое обобщение основной черты характера скалы 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("ab", 6.0), а не просто двойником.

ОК, в этом конкретном примере вы на самом деле не так много получаете, потому что получение «суммы» имен безопасности не очень полезно. Но для других классов (например, если у вас есть количество, а также цена или несколько соответствующих свойств), это может быть очень полезно. По большому счету замечательно, что если вы можете определить какой-то способ добавления двух экземпляров вашего класса вместе, вы можете рассказать об этом скалазу (определив Semigroup); и если вы также можете определить нулевой элемент, вы можете использовать это определение для простого суммирования коллекций вашего класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...