對於任何特定類型的數字(即Double/Integer)來說似乎都很容易出現問題,但在一般情況下很難寫入。如何在scala中實現泛型平均函數?
implicit def iterebleWithAvg(data:Iterable[Double]) = new {
def avg:Double = data.sum/data.size
}
如何爲任何類型的數字(Int,Float,Double,BigDecemial)實現此?
對於任何特定類型的數字(即Double/Integer)來說似乎都很容易出現問題,但在一般情況下很難寫入。如何在scala中實現泛型平均函數?
implicit def iterebleWithAvg(data:Iterable[Double]) = new {
def avg:Double = data.sum/data.size
}
如何爲任何類型的數字(Int,Float,Double,BigDecemial)實現此?
你要通過一個隱含Numeric
這將使求和並轉換爲雙:
def average[T](ts: Iterable[T])(implicit num: Numeric[T]) = {
num.toDouble(ts.sum)/ts.size
}
編譯器會爲您提供正確的實例:
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
您可以使用該功能定義隱式視圖(假設您傳播隱式數字依賴關係):
implicit def iterebleWithAvg[T:Numeric](data:Iterable[T]) = new {
def avg = average(data)
}
scala> List(1,2,3,4).avg
res13: Double = 2.5
這是我在代碼中定義它的方式。
代替使用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)
注意如何,如果我們給它的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
然而,Int
是不是一種Fractional
,這意味着它沒有意義得到一個Int
和平均Int
S的結果,所以我們必須有一個特殊的情況下Int
要轉換爲一個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)
所以平均Int
s給出我們Double
:
scala> Iterator(1, 2, 3, 4, 5).avg
res2: Double = 3
'隱類IterebleWithAvg [T:數值](數據:可迭代[T]){DEF AVG =平均值(數據)}'應今天是首選。 – 2014-04-25 16:35:46
我們還應該處理零長度迭代,拋出一個錯誤。 – Marboni 2016-01-09 22:03:55