【发布时间】:2017-09-08 23:40:46
【问题描述】:
我想实现一个通用的加权平均函数,它放宽了对值和权重为同一类型的要求。即,我想支持说:(value:Float,weight:Int) 和(value:Int,weight:Float) 参数的序列,而不仅仅是:(value:Int,weight:Int)。 [在此之前,请参阅我之前的 question。]
这是我目前拥有的:
def weightedSum[A: Numeric](weightedValues: GenSeq[(A, A)]): (A, A)
def weightedAverage[A: Numeric](weightedValues: GenSeq[(A, A)]): A = {
val (weightSum, weightedValueSum) = weightedSum(weightedValues)
implicitly[Numeric[A]] match {
case num: Fractional[A] => ...
case num: Integral[A] => ...
case _ => sys.error("Undivisable numeric!")
}
}
例如,如果我喂它,这将非常有效:
val values:Seq[(Float,Float)] = List((1,2f),(1,3f))
val avg= weightedAverage(values)
但是,如果我不将权重从Int“向上转换”到Float:
val values= List((1,2f),(1,3f)) //scalac sees it as Seq[(Int,Float)]
val avg= weightedAverage(values)
Scala 编译器会告诉我:
错误:找不到类型的证据参数的隐式值 数值[AnyVal]
val avg= weightedAverage(values)
有没有办法解决这个问题?
我曾尝试编写一个NumericCombine 类,我用A 和B 参数化了该类,它将这些类型“组合”成一个“通用”类型AB(例如,组合Float 和@ 987654343@ 给你Float) :
abstract class NumericCombine[A: Numeric, B: Numeric] {
type AB <: AnyVal
def fromA(x: A): AB
def fromB(y: B): AB
val num: Numeric[AB]
def plus(x: A, y: B): AB = num.plus(fromA(x), fromB(y))
def minus(x: A, y: B): AB = num.minus(fromA(x), fromB(y))
def times(x: A, y: B): AB = num.times(fromA(x), fromB(y))
}
并且我设法在此基础上使用 typeclass 模式编写了简单的 times 和 plus 函数,但是由于 NumericCombine 引入了路径依赖类型 AB,因此“组合”类型被证明是更多比我预想的要难。查看this 问题了解更多信息,并查看here 了解NumericCombine 的完整实现。
更新
作为对another question(完整工作演示here)的回答,已经获得了一个稍微令人满意的解决方案,但是考虑到@ziggystar 在discussion 中提出的观点,仍有一些设计改进的空间。
【问题讨论】:
-
是的,您遇到的问题类似于不久前的something I asked about。我找不到满意的答案。
-
@jwvh 谢谢。是的,我遇到了这个问题。我不明白为什么添加的隐式没有做任何事情。
-
@sailfish009 感谢您的链接。是的,我了解隐含证据参数的工作原理。我试图让 scalac 不选择
Int和Float=AnyVal的最低分母,因为显然没有Numeric[AnyVal]。 -
@jwvh 这会起作用:stackoverflow.com/questions/43392597/…
标签: scala generics implicit-conversion implicit