2012-04-15 14 views
17

特定の種類のNumber、つまりDouble/Integerでは簡単に問題があるようですが、一般的な場合は書きにくいです。番号(INT、フロート、ダブル、BigDecemial)のいずれかの種類のためにこれを実装する方法スカラーで汎用平均関数を実装する方法は?

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

答えて

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:数値](データ:のIterable [T]){DEF =平均(データ)avgを}'べき今日のように好まれる。 –

+0

また、ゼロ長の繰り返しが可能で、エラーが発生します。 – Marboni

13

これは私のコードでそれを定義する方法です。

代わりのNumericを用いFractionalが除算演算を(Numericは必ずしも分割していません)を定義しているので、私は、Fractionalを使用します。つまり、.avgに電話すると、いつもDoubleになるのではなく、入力したのと同じタイプが返されます。

Iteratorなどで動作するように、すべてGenTraversableOnceコレクションにも定義します。

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は、それがIntInt Sを平均した結果を取得しても意味がないことを意味し、Fractionalの一種ではありませんので、我々はに変換し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) 

だからInt Sを平均すること私たちDoubleを与える:

scala> Iterator(1, 2, 3, 4, 5).avg 
res2: Double = 3 
関連する問題