ShS*_*ShS 8 generics scala implicit implicit-conversion
我想实现一个通用加权平均函数,它放宽了对值的要求,并且权重属于同一类型.即,我想支持说:(value:Float,weight:Int)和(value:Int,weight:Float)参数的序列,而不仅仅是:(value:Int,weight:Int).[请参阅我之前提到的问题.]
这就是我目前拥有的:
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!")
}
}
Run Code Online (Sandbox Code Playgroud)
如果我喂它,例如:
val values:Seq[(Float,Float)] = List((1,2f),(1,3f))
val avg= weightedAverage(values)
Run Code Online (Sandbox Code Playgroud)
但是,如果我不"重叠"权重Int到Float:
val values= List((1,2f),(1,3f)) //scalac sees it as Seq[(Int,Float)]
val avg= weightedAverage(values)
Run Code Online (Sandbox Code Playgroud)
Scala编译器会告诉我:
错误:无法找到Numeric类型的证据参数的隐含值[AnyVal]
val avg = weightedAverage(values)
有办法绕过这个吗?
我试图编写一个NumericCombine我参数化的类,A并将B这些类型"组合"成"通用"类型AB(例如,组合Float并Int给你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))
}
Run Code Online (Sandbox Code Playgroud)
我设法用类型类型模式编写基于此的简单times和plus函数,但由于NumericCombine引入了路径依赖类型AB,"组合"类型证明比我预期的更难.请查看此问题以获取更多信息,并在此处查看完整实施NumericCombine.
更新
作为另一个问题(这里的完整工作演示)的答案,已经获得了一个令人满意的解决方案,但是考虑到与@ziggystar 讨论中提出的观点,仍然存在一些设计改进的空间.
T我认为涉及通过类型标量来衡量/缩放某些类型元素的更一般任务S是线性组合。以下是某些任务的权重限制:
因此,根据这种分类,最常见的情况是线性组合。根据维基百科,它要求权重S为一个域,并在 上T形成一个向量空间S。
编辑:您对类型的真正最普遍的要求是在环上T形成一个模块(wiki)S,或者T成为一个S模块。
您可以使用类型类来设置这些要求。还有spire,它已经有Field和的类型类VectorSpace。我自己没用过,所以你必须自己检查一下。
Float/Int不起作用从这个讨论中还可以明显看出,以及您已经观察到的事实是,作为Float权重和Int元素类型是行不通的,因为整数不会在实数上形成向量空间。你必须Int首先晋升Float。
标量类型只有两个主要候选者,即Float和Double。主要只是Int晋升的候选者,因此您可以执行以下操作作为简单且不那么通用的解决方案:
case class Promotable[R,T](promote: R => T)
object Promotable {
implicit val intToFloat = Promotable[Int,Float](_.toFloat)
implicit val floatToDouble = Promotable[Float,Double](_.toDouble)
implicit val intToDouble = Promotable[Int,Double](_.toDouble)
implicit def identityInst[A] = Promotable[A,A](identity)
}As a "small" solution you could write a typeclass
def weightedAverage[S,VS](values: Seq[(S,VS)])(implicit p: Promotable[VS,S]) = ???
Run Code Online (Sandbox Code Playgroud)