我正在尝试编写一个泛型平均函数,该函数在包含数字类型的Iterable上运行。例如,它将在阵列上运行,如下所示:
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))
等,希望能够处理其他迭代,例如列表。
我尝试了各种咒语的方法,但无济于事:
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
在Scala中执行此操作的正确方法是什么?
答案 0 :(得分:17)
这有效:
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!")
}
所以,让我们做一些解释。首先,必须在类型类模式中使用Numeric
。也就是说,您没有说T
类型,或者可以转换为Numeric
。相反,Numeric
提供了类型T
上的方法。其中一个例子是num.fromInt
。
接下来,Numeric
不提供公共除法运算符。相反,必须在Fractional
和Integral
之间进行选择。在这里,我匹配Numeric[T]
以区分两者。
请注意,我不会在匹配项上使用T
,因为Scala无法检查匹配项上的类型参数,因为它们已被删除。相反,我使用_
,如果可能,Scala会推断出正确的类型(就像在这里一样)。
之后,我正在导入num._
,其中num
为Fractional
或Integral
。这会将一些隐式转换带入上下文,让我可以直接调用方法/
。如果我不做那个导入,我会被迫写这个:
num.div(xs.sum, num.fromInt(xs.size))
请注意,我不必将隐式参数传递给xs.sum
,因为它已在范围内隐式提供。
我猜就是这样。我错过了什么吗?
答案 1 :(得分:7)
你的一个版本非常接近:
def mean[T](xs: Iterable[T])(implicit num: Numeric[T]):Double =
num.toDouble(xs.sum) / xs.size
以下是其他语法:
def mean[T: Numeric](xs: Iterable[T]):Double =
implicitly[Numeric[T]].toDouble(xs.sum) / xs.size
答案 2 :(得分:4)
def mean[A](it:Iterable[A])(implicit n:Numeric[A]) = {
it.map(n.toDouble).sum / it.size
}
答案 3 :(得分:0)
这是一个很老的问题,但我基本上是在做这个
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
}
}
表示可能包含null
值的列表(我必须保持与Java的互操作性)。 null
元素不计入平均值。