我想实现一个通用加权平均函数,它放宽了对值的要求,并且权重属于同一类型。即,我想支持说:(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
和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
,“编写“事实证明这种类型比我预期的要困难。请查看this问题以获取更多信息,并查看here以了解NumericCombine
的完整实施情况。
更新
作为another question(完整工作演示here)的答案,已经获得了一个令人满意的解决方案,但考虑到{{3}中提出的问题,仍然有一些设计改进的余地。与@ziggystar。
答案 0 :(得分:3)
我认为通过类型T
的标量对S
类型的某些元素进行称重/缩放的更一般的任务是线性组合。以下是某些任务的权重约束:
因此,根据此分类的最一般情况是线性组合。
根据维基百科,它要求权重S
为字段,而T
要求vector space超过S
。
修改:您对类型的最常见要求是T
在S
上形成module (wiki),或{{1成为T
- 模块。
您可以使用类型类来设置这些要求。还有spire,它已经有S
和Field
的类型类。我自己从未使用过它,所以你必须自己检查一下。
VectorSpace
/ Float
无法工作从这次讨论中可以看出,以及你已经观察到的事实是,Int
作为权重,而Float
作为元素类型将无法解决,因为整体数字不会在实数上形成向量空间。您必须先将Int
提升为Int
。
标量类型只有两个主要候选者,即Float
和Float
。
并且主要只有Double
是推广的候选者,所以你可以做以下简单而不是一般的解决方案:
Int
答案 1 :(得分:-1)
首先你的模板是错误的。 (对不起,如果&#34;模板&#34;表达式错误 - 我是scala的新手)。 你的函数需要两个元素属于同一类型的元组([A:Numeric]),而不是元素属于不同类型的元组([A:数字,B:数字])((Int,Float)vs(Float,浮动))
无论如何,下面的编译并希望在你用你想要的微积分填充之后能够很好地工作。
import scala.collection._
def weightedSum[A: Numeric, B: Numeric](weightedValues: GenSeq[(A,B)]): (A,B) = {
weightedValues.foldLeft((implicitly[Numeric[A]].zero, implicitly[Numeric[B]].zero)) { (z, t) =>
( implicitly[Numeric[A]].plus(z._1, t._1),
implicitly[Numeric[B]].plus(z._2, t._2)
)
}
}
def weightedAverage[A: Numeric, B: Numeric](weightedValues: GenSeq[(A,B)]): A = {
val (weightSum, weightedValueSum) = weightedSum(weightedValues)
implicitly[Numeric[A]] match {
case num: Fractional[A] => implicitly[Numeric[A]].zero
case num: Integral[A] => implicitly[Numeric[A]].zero
case _ => sys.error("Undivisable numeric!")
}
}
val values1: Seq[(Float, Float)] = List((1, 2f), (1, 3f))
val values2: Seq[(Int, Float)] = List((1, 2f), (1, 3f))
val wa1 = weightedAverage(values1)
val wa2 = weightedAverage(values2)