加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala:通用加权平均函数

发布时间:2020-12-16 09:26:39 所属栏目:安全 来源:网络整理
导读:我想实现一个通用加权平均函数,它放宽了对值的要求,并且权重属于同一类型.即,我想支持以下序列:(值:Float,weight:Int)和(value:Int,weight:Float)参数,而不仅仅是:(值:Int,weight:Int). [在此之前见到我之前的 question.] 这就是我目前所拥有的: def
我想实现一个通用加权平均函数,它放宽了对值的要求,并且权重属于同一类型.即,我想支持以下序列:(值:Float,weight:Int)和(value:Int,weight:Float)参数,而不仅仅是:(值: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,3f)) //scalac sees it as Seq[(Int,Float)] 
val avg= weightedAverage(values)

Scala编译器会告诉我:

error: could not find implicit value for evidence parameter of type
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))
}

我设法用类型类模式编写基于此的简单时间和函数,但由于NumericCombine引入了路径依赖类型AB,因此“组合”类型证明比我预期的更难.有关更多信息,请参阅this问题,有关NumericCombine的完整实施,请参阅here.

更新

作为another question(完整工作演示here)的答案,已经获得了一个令人满意的解决方案,但是考虑到discussion中@ziggystar提出的要点,仍然有一些设计改进的空间.

解决方法

线性组合

我认为涉及通过类型S的标量对T类型的某些元素进行称重/缩放的更一般的任务是线性组合.以下是某些任务的权重约束:

> linear combination:没有限制
>仿射组合:权重总和为一
> canonical combination/weighted average:权重是非负的
>凸组合:权重总和为1并且是非负的

因此,根据该分类的最一般情况是线性组合.
根据维基百科,它需要权重S是一个字段,而T要求在S上形成一个vector space.

编辑:您对类型的最常见要求是T在环S上形成module (wiki),或者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]) = ???

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读