# Scala：在隐式参数中抽象依赖于路径的类型

``````abstract class NumericCombine[A:Numeric,B:Numeric]{
type AB <: AnyVal
}
``````

``````def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B].AB
``````

``````implicit object IntFloat extends NumericCombine[Int,Float]{override type AB = Float}
implicit object FloatInt extends NumericCombine[Float,Int]{override type AB = Float}
``````

``````def plus[A: Numeric,B:Numeric](x: A, y: B): NumericCombine[A,B].AB =
{
type AB = Numeric[NumericCombine[A,B].AB]
implicitly[AB].plus(x.asInstanceOf[AB],y.asInstanceOf[AB])
}

plus(1f,2)//=3f
plus(1,2f)//=3f
``````

``````def plus[T](a: T, b: T)(implicit ev:Numeric[T]): T = ev.plus(a,b)
``````

`NumericCombine`现在看起来像这样：

``````abstract class NumericCombine[A: Numeric, B: Numeric] {
type AB <: AnyVal

def fromA(x: A): AB
def fromB(y: B): AB

val numeric: Numeric[AB]

def plus(x: A, y: B): AB = numeric.plus(fromA(x), fromB(y))
def minus(x: A, y: B): AB = numeric.minus(fromA(x), fromB(y))
def times(x: A, y: B): AB = numeric.times(fromA(x), fromB(y))
}
``````

``````def plus[A: Numeric, B: Numeric](x: A, y: B)(implicit ev:NumericCombine[A,B])
: ev.AB = ev.plus(x, y)
``````

``````def accumulateWeightedValue[A: Numeric,B: Numeric]
(accum: (A, NumericCombine[A, B]#AB), ValueWithWeight: (A, B))
(implicit combine: NumericCombine[A, B], timesNumeric: Numeric[NumericCombine[A, B]#AB])
:(A,NumericCombine[A, B]#AB)=
``````

``````def weightedSum[A: Numeric,B: Numeric](weightedValues: GenTraversable[(A, B)])
(implicit numericCombine: NumericCombine[A, B], plusNumeric: Numeric[NumericCombine[A, B]#AB])
: (A, NumericCombine[A, B]#AB)
``````

``````val lst: Seq[(Int, Float)] =List((1,3f),(1,4f))
implicit val num: Numeric[Float] = IntFloat.numeric //IntFloat extends NumericCombine[Int,Float]
weightedSum(lst)
``````

PH88

* 2017 年 4 月 18 日：根据作者的最新代码进行更新 *

* 2017 年 4 月 19 日 *

• 添加`NumericCombine#Implicits`以备不时之需
• 删除`AnyVal`约束以支持任何`Numeric`类型，例如`BigInt`
• 重构 `NumericCombine`

``````import scala.collection.GenSeq

trait NumericCombine[A, B] {
type AB

def fromA(x: A): AB

def fromB(y: B): AB

val numericA: Numeric[A]
val numericB: Numeric[B]
val numericAB: Numeric[AB]

// For convenience, caller can 'import combine.Implicits._'
// to bring the Numeric's into the current scope
object Implicits {
implicit def implicitNumericA = numericA

implicit def implicitNumericB = numericB

implicit def implicitNumericAB = numericAB
}

def plus(x: A, y: B): AB = numericAB.plus(fromA(x), fromB(y))

def minus(x: A, y: B): AB = numericAB.minus(fromA(x), fromB(y))

def times(x: A, y: B): AB = numericAB.times(fromA(x), fromB(y))
}

object NumericCombine {
type Aux[A, B, _AB] = NumericCombine[A, B] {
type AB = _AB
}

private def combine[A, B, _AB](fa: A => _AB, fb: B => _AB)
(implicit
_numericA: Numeric[A],
_numericB: Numeric[B],
_numericAB: Numeric[_AB]
): NumericCombine[A, B] = new NumericCombine[A, B] {
override type AB = _AB

override def fromA(x: A): AB = fa(x)

override def fromB(y: B): AB = fb(y)

override val numericA: Numeric[A] = _numericA
override val numericB: Numeric[B] = _numericB
override val numericAB: Numeric[AB] = _numericAB
}

implicit lazy val IntFloat  = combine[Int, Float, Float](_.toFloat, identity)
implicit lazy val BigIntBigDecimal  = combine[BigInt, BigDecimal, BigDecimal](i => BigDecimal(i), identity)

}

implicit class ValuesWithWeight[A, B](val weightedValue: (A, B)) {
def weight: A = weightedValue._1

def value: B = weightedValue._2
}

def weightedSum[A, B, AB]
(valuesWithWeight: GenSeq[(A, B)])
(implicit combine: NumericCombine.Aux[A, B, AB]):
(A, AB) = {

import combine.Implicits._

val z: (A, AB) =
(combine.numericA.zero, combine.numericAB.zero)

def accumulateWeightedValue(accum: (A, AB), valueWithWeight: (A, B)): (A, AB) = {
val weightedValue = combine.times(valueWithWeight.weight, valueWithWeight.value)
(
combine.numericA.plus(accum.weight, valueWithWeight.weight),
combine.numericAB.plus(accum.value, weightedValue)
)
}

valuesWithWeight.aggregate(z)(
accumulateWeightedValue,
// dataOps.tuple2.plus[A,AB]
{
case ((a1, ab1), (a2, ab2)) =>
(combine.numericA.plus(a1, a2) ->
combine.numericAB.plus(ab1, ab2))
}
)
}

weightedSum(Seq(1 -> 1.5f, 2 -> 1f, 3 -> 1.7f))
weightedSum(Seq(BigInt(1) -> BigDecimal("1.5"), BigInt(2) -> BigDecimal("1"), BigInt(3) -> BigDecimal("1.7")))
``````

0 条评论