特定の種類の数値、つまりDouble / Integerにとっては簡単な問題のようですが、一般的なケースでは記述が困難です。
implicit def iterebleWithAvg(data:Iterable[Double]) = new {
def avg:Double = data.sum / data.size
}
これを任意の種類のNumber(Int、Float、Double、BigDecemial)に実装するにはどうすればよいですか?
特定の種類の数値、つまりDouble / Integerにとっては簡単な問題のようですが、一般的なケースでは記述が困難です。
implicit def iterebleWithAvg(data:Iterable[Double]) = new {
def avg:Double = data.sum / data.size
}
これを任意の種類のNumber(Int、Float、Double、BigDecemial)に実装するにはどうすればよいですか?
Numeric
合計と Double への変換を許可する暗黙を渡す必要があります。
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
は一種ではありません。つまり、sを平均化した結果Fractional
を取得することは意味がないため、をに変換するための特別なケースが必要です。Int
Int
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