Scala中的通用类型`toDouble`

时间:2017-05-23 09:23:28

标签: scala generics

嗨,如下所示。 它应该是一个计算序列平均值的函数。

 def fun1[T <: { def toDouble:Double }] (seq:Seq[T]) = {
    val q2 = seq map { _.toDouble }
    val n = q2.size.toDouble
    q2.sum / n
  }

但我无法将Seq(1,2,3)传递给fun1。 为什么呢?

2 个答案:

答案 0 :(得分:2)

JVM上的结构类型非常昂贵,您应该避免使用它们。按理说,只有Numeric可以编码为Double。 Scala提供了Numeric类型类作为在所谓的“数字类型”上定义泛型操作的方法,因此您实际上不需要使用您提出的内容,标准库已经涵盖了您。

def fun1[T](seq: Seq[T])(ev: Numeric[T]): Double = {
  val q2 = seq map ev.toDouble
  val n = q2.size.toDouble
  q2.sum / n
}

如果你想要的只是你在数字上的通用操作,你真的不需要获得更多的创意。否则,您将必须实现自己的类型类。

trait Converter[T] {
  def toDouble(source: T): Double
}
object Converter {
  implicit def numericConvert[T : Numeric]: Converter[T] = new Converter[T] { def toDouble(source: T): Double = implicitly[Numeric[T]].toDouble(source)}
  implicit object StringConverter extends Converter[String] = ...
} 

现在使用基于Numeric的简单方法,您可以定义一个简单的帮助器。

object Helper {
  implicit class ColOps[
    M[X] <: TraversableOnce[X],
    T
  ](val col: M[T])(implicit ev: Numeric[T]) {
    def average: Double = {
      val q2 = col map ev.toDouble
      val n = q2.size.toDouble
      q2.sum / n
    }
  }
}

现在你需要做的就是:

   import Helper._

   List(1, 2, 3).average
   Seq(1, 2, 3).average
   Set(5F, 2F, 3F).average

这有点酷,因为我们也可以抽象出常见的集合类型。

答案 1 :(得分:0)

def fun1[T <: AnyVal{ def toDouble:Double }] (seq:Seq[T]) = {
  val q2 = seq map { _.toDouble }
  val n = q2.size.toDouble
  q2.sum / n
}

fun1(Seq(1, 2, 3))

似乎编译器将类型推断为AnyRef。将其定义为AnyVal可行。 AnyRef是所有对象的超类型,而AnyVal是所有基元的超类型。