yur*_*ura 18 scala scala-collections
对于任何特定类型的数字,即Double/Integer,它似乎很容易出问题,但在一般情况下很难写.
implicit def iterebleWithAvg(data:Iterable[Double]) = new {
def avg:Double = data.sum / data.size
}
Run Code Online (Sandbox Code Playgroud)
如何为任何类型的数字(Int,Float,Double,BigDecemial)实现这个?
par*_*tic 32
你必须传递一个隐含的Numeric,它允许求和和转换为Double:
def average[T]( ts: Iterable[T] )( implicit num: Numeric[T] ) = {
num.toDouble( ts.sum ) / ts.size
}
Run Code Online (Sandbox Code Playgroud)
编译器将为您提供正确的实例:
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
Run Code Online (Sandbox Code Playgroud)
您可以使用该函数来定义隐式视图(假设您传播隐式数字依赖项):
implicit def iterebleWithAvg[T:Numeric](data:Iterable[T]) = new {
def avg = average(data)
}
scala> List(1,2,3,4).avg
res13: Double = 2.5
Run Code Online (Sandbox Code Playgroud)
dhg*_*dhg 14
这是我在代码中定义它的方式.
Numeric我使用Fractional,而不是使用,因为Fractional定义了一个除法运算(Numeric不一定有除法).这意味着当你打电话时.avg,你会得到你输入的相同类型,而不是总是得到Double.
我还在所有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)
Run Code Online (Sandbox Code Playgroud)
请注意,如果我们给它一个集合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
Run Code Online (Sandbox Code Playgroud)
然而,Int这不是一种Fractional,意味着得到a Int和平均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)
Run Code Online (Sandbox Code Playgroud)
所以平均值Int给了我们一个Double:
scala> Iterator(1, 2, 3, 4, 5).avg
res2: Double = 3
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12332 次 |
| 最近记录: |