我想实现一个通用加权平均函数,它放宽了对值的要求,并且权重属于同一类型 . 即,我想支持以下序列: (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
的权重"upcast":
val values= List((1,2f),(1,3f)) //scalac sees it as Seq[(Int,Float)]
val avg= weightedAverage(values)
Scala编译器会告诉我:
错误:无法找到Numeric类型的证据参数的隐含值[AnyVal] val avg = weightedAverage(values)
有办法绕过这个吗?
我尝试编写一个 NumericCombine
类,我用 A
和 B
参数化了"combines"类型"common"类型 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))
}
我设法使用类型类型模式编写简单的 times
和 plus
函数,但由于 NumericCombine
引入了路径依赖类型 AB
,"composing"这些类型证明比我预期的更难 . 请查看this问题以获取更多信息,并参见here以完整实施 NumericCombine
.
Update
作为another question(完整的工作演示here)的答案已经获得了一个令人满意的解决方案,但考虑到@ziggystar在discussion中提出的要点,仍然有一些设计改进的空间 .
2 回答
首先,你的模板是错误的 . (对不起,如果“模板”表达式错误 - 我是scala的新手) . 你的函数需要两个元素属于同一类型的元组([A:Numeric]),而不是元素属于不同类型的元组([A:数字,B:数字])((Int,Float)vs(Float,浮动))
无论如何,下面编译并希望在你用你想要的微积分填充它之后会很好地工作 .
线性组合
我认为通过
S
类型的标量对T
类型的某些元素进行称重/缩放的更一般的任务是线性组合 . 以下是某些任务的权重约束:linear combination:没有约束
仿射组合:权重总和为1
canonical combination/weighted average:权重是非负的
凸组合:权重总和为1且非负数
因此,根据该分类的最一般情况是线性组合 . 根据维基百科,它需要权重
S
为一个字段,而T
要求vector space超过S
.Edit: 对类型的真正最普遍的要求是
T
在环S
上形成module (wiki),或者T
是S
模块 .Spire
您可以使用类型类来设置这些要求 . 还有spire,它已经有
Field
和VectorSpace
的类型类 . 我自己从未使用过它,所以你必须自己检查一下 .Float / Int不起作用
从这个讨论中可以明显看出,你已经观察到的是,将
Float
作为权重,而Int
作为元素类型将无法解决,因为整数不会在实数上形成向量空间 . 你必须首先将Int
推广到Float
.通过类型类推广
标量类型只有两个主要候选者,即
Float
和Double
. 并且主要只有Int
是推广的候选者,因此您可以将以下内容作为一个简单且不那么通用的解决方案: