如何在 Scala 中实现通用平均函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10160280/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to implement generic average function in scala?
提问by yura
It seems easy problem for any specific kind of Number i.e. Double/Integer but it is hard to write in general case.
对于任何特定类型的数字,即双精度/整数,这似乎是一个简单的问题,但在一般情况下很难编写。
implicit def iterebleWithAvg(data:Iterable[Double]) = new {
def avg:Double = data.sum / data.size
}
How to implement this for any kind of Number(Int,Float,Double,BigDecemial)?
如何为任何类型的 Number(Int,Float,Double,BigDecemial) 实现这个?
回答by paradigmatic
You have to pass an implicit Numericwhich will allow summation and conversion to Double:
您必须传递一个隐式Numeric,它允许求和并转换为 Double:
def average[T]( ts: Iterable[T] )( implicit num: Numeric[T] ) = {
num.toDouble( ts.sum ) / ts.size
}
The compiler will provide the correct instance for you:
编译器将为您提供正确的实例:
scala> average( List( 1,2,3,4) )
res8: Double = 2.5
scala> average( 0.1 to 1.1 by 0.05 )
res9: Double = 0.6000000000000001
scala> average( Set( BigInt(120), BigInt(1200) ) )
res10: Double = 660.0
You can the use the function to define an implicit view (provided you propagate the implicit numeric dependency):
您可以使用该函数定义隐式视图(前提是您传播隐式数字依赖项):
implicit def iterebleWithAvg[T:Numeric](data:Iterable[T]) = new {
def avg = average(data)
}
scala> List(1,2,3,4).avg
res13: Double = 2.5
回答by dhg
Here's the way I define it in my code.
这是我在代码中定义它的方式。
Instead of using Numeric, I use Fractional, since Fractionaldefines a division operation (Numericdoesn't necessarily have division). This means that when you call .avg, you will get back the same type you put in, instead of always getting Double.
Numeric我没有使用Fractional,而是使用,因为Fractional定义了除法运算(Numeric不一定有除法)。这意味着当您调用 时.avg,您将返回您输入的相同类型,而不是总是得到Double.
I also define it over all GenTraversableOncecollections so that it works on, for example, Iterator.
我还在所有GenTraversableOnce集合上定义它,以便它适用于,例如,Iterator.
class EnrichedAvgFractional[A](self: GenTraversableOnce[A]) {
def avg(implicit num: Fractional[A]) = {
val (total, count) = self.toIterator.foldLeft((num.zero, num.zero)) {
case ((total, count), x) => (num.plus(total, x), num.plus(count, num.one))
}
num.div(total, count)
}
}
implicit def enrichAvgFractional[A: Fractional](self: GenTraversableOnce[A]) = new EnrichedAvgFractional(self)
Notice how if we give it a collection of Double, we get back Doubleand if we give it BigDecimal, we get back BigDecimal. We could even define our own Fractionalnumber type (which I do occasionally), and it will work for that.
请注意,如果我们给它一个集合Double,我们会回来Double,如果我们给它BigDecimal,我们会回来BigDecimal。我们甚至可以定义我们自己的Fractional数字类型(我偶尔会这样做),它会为此工作。
scala> Iterator(1.0, 2.0, 3.0, 4.0, 5.0).avg
res0: Double = 3.0
scala> Iterator(1.0, 2.0, 3.0, 4.0, 5.0).map(BigDecimal(_)).avg
res1: scala.math.BigDecimal = 3.0
However, Intis not a kind of Fractional, meaning that it doesn't make sense to get an Intand the result of averaging Ints, so we have to have a special case for Intthat converts to a Double.
然而,Int不是一种Fractional,这意味着获得 anInt和平均Ints的结果没有意义,因此我们必须有一个特殊情况Int,将其转换为 a Double。
class EnrichedAvgInt(self: GenTraversableOnce[Int]) {
def avg = {
val (total, count) = self.toIterator.foldLeft(0, 0) {
case ((total, count), x) => (total + x, count + 1)
}
total.toDouble / count
}
}
implicit def enrichAvgInt(self: GenTraversableOnce[Int]) = new EnrichedAvgInt(self)
So averaging Ints gives us a Double:
所以平均Ints 给了我们一个Double:
scala> Iterator(1, 2, 3, 4, 5).avg
res2: Double = 3

