12

I'm trying to write a generic mean function that operates on an Iterable that contains numeric types. It would operate, say, on arrays, as so:

val rand = new scala.util.Random()
val a = Array.fill(1000) { rand.nextInt(101) }
val b = Array.fill(1000) { rand.nextDouble }

println(mean(a))
println(mean(b))

etc., hopefully being able to work on other iterables, such as lists.

I have tried various incantations for the mean method, to no avail:

def mean[T <% Numeric[T]](xs: Iterable[T]) = xs.sum.toDouble / xs.size
def mean[A](xs: Iterable[Numeric[A]]):Double = xs.sum.toDouble / xs.size
def mean[T](xs: Iterable[T])(implicit num: Numeric[T]):Double = xs.sum / xs.size
def mean(xs: Iterable[Double]) = xs.sum / xs.size

What is the proper way to do this in Scala?

Bill Lear
  • 141
  • 2
  • 4

4 Answers4

18

This works:

def mean[T : Numeric](xs: Iterable[T]): T = implicitly[Numeric[T]] match {
    case num: Fractional[_] => import num._; xs.sum / fromInt(xs.size)
    case num: Integral[_] => import num._; xs.sum / fromInt(xs.size)
    case _ => sys.error("Undivisable numeric!")
}

So, let's make some explanations. First, Numeric must be used in type class pattern. That is, you don't say a type T is, or can be converted into, Numeric. Instead, Numeric provides methods over a type T. One such example is num.fromInt.

Next, Numeric does not provide a common division operator. Instead, one must choose between Fractional and Integral. Here, I'm matching on Numeric[T] to distinguish between both.

Note that I don't use T on the match, because Scala cannot check for type parameters on matches, as they are erased. Instead, I use _, and Scala infers the correct type if possible (as it is here).

After that, I'm importing num._, where num is either Fractional or Integral. This brings some implicit conversions into context that let me do stuff like calling the method / directly. If I did not do that import, I'd be forced to write this:

num.div(xs.sum, num.fromInt(xs.size))

Note that I do not have to pass the implicit parameter to xs.sum, since it is already implicitly available in the scope.

I guess that's it. Did I miss anything?

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
8

One of your version is pretty close:

def mean[T](xs: Iterable[T])(implicit num: Numeric[T]):Double = 
  num.toDouble(xs.sum) / xs.size

Here is the other syntax:

def mean[T: Numeric](xs: Iterable[T]):Double =
  implicitly[Numeric[T]].toDouble(xs.sum) / xs.size
huynhjl
  • 41,520
  • 14
  • 105
  • 158
  • 1
    In Scala 2.9, if you do `import Numeric.Implicits._`, then you can just write `xs.sum.toDouble / xs.size`. I've edited the answer to say so. – Seth Tisue May 31 '11 at 16:24
4
def mean[A](it:Iterable[A])(implicit n:Numeric[A]) = {
  it.map(n.toDouble).sum / it.size
}
Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
  • I think SO has some latency/consistency delay recently. It seems some questions have only one or no answers for a while and then suddenly a bunch show up. – huynhjl May 31 '11 at 18:16
0

This is quite an old question, but I am basically doing this

def average[A](list: List[Any])(implicit numerics: Numeric[A]): Double = {
  list.map(Option(_)).filter(_.isDefined).flatten match {
    case Nil => 0.0
    case definedElements => numerics.toDouble(list.map(_.asInstanceOf[A]).sum) / definedElements.length.toDouble
  }
}

for a list which might contain null values (I have to keep interoperability with Java). The null elements are not counted towards the average.

gire
  • 1,105
  • 1
  • 6
  • 16