2012-04-15 107 views
17

對於任何特定類型的數字(即Double/Integer)來說似乎都很容易出現問題,但在一般情況下很難寫入。如何在scala中實現泛型平均函數?

implicit def iterebleWithAvg(data:Iterable[Double]) = new { 
    def avg:Double = data.sum/data.size 
} 

如何爲任何類型的數字(Int,Float,Double,BigDecemial)實現此?

回答

29

你要通過一個隱含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 
+2

'隱類IterebleWithAvg [T:數值](數據:可迭代[T]){DEF AVG =平均值(數據)}'應今天是首選。 – 2014-04-25 16:35:46

+0

我們還應該處理零長度迭代,拋出一個錯誤。 – Marboni 2016-01-09 22:03:55

13

這是我在代碼中定義它的方式。

代替使用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