Writing a generic mean function in Scala

前端 未结 4 1485
轮回少年
轮回少年 2020-12-14 22:41

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 = n         


        
相关标签:
4条回答
  • 2020-12-14 23:22

    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
    
    0 讨论(0)
  • 2020-12-14 23:23
    def mean[A](it:Iterable[A])(implicit n:Numeric[A]) = {
      it.map(n.toDouble).sum / it.size
    }
    
    0 讨论(0)
  • 2020-12-14 23:27

    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?

    0 讨论(0)
  • 2020-12-14 23:37

    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.

    0 讨论(0)
提交回复
热议问题