简体   繁体   English

Scala中的通用类型`toDouble`

[英]Generic Type `toDouble` in Scala

Hi have the following as bellow. 嗨,下面是波纹管。 It is supposed to be a function that calculate the average of a sequence. 它应该是计算序列平均值的函数。

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

However I cannot pass Seq(1,2,3) to fun1 . 但是我不能将Seq(1,2,3)传递给fun1 Why? 为什么?

Structural types are very expensive on the JVM and you should avoid them. 结构类型在JVM上非常昂贵,您应该避免使用它们。 It stands to reason that only a Numeric could be encoded as a Double . 有理由认为,只有Numeric可以被编码为Double Scala offers a Numeric typeclass as a way to define generic operations on the so called "number types", so you don't actually need to use what you are proposing, the standard lib has you covered. 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
}

You don't really need to get more creative than that if all you want is your operation to be generic on numbers. 如果您想要的只是对数字进行通用操作,那么您实际上并不需要更具创造力。 Otherwise, you will have to implement your own typeclass. 否则,您将必须实现自己的类型类。

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] = ...
} 

Now to use the simple approach based on Numeric , you could define a simple helper. 现在,要使用基于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
    }
  }
}

Now all you need to do is: 现在您需要做的是:

   import Helper._

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

This is a bit more cool, since we can abstract over common collection types too. 这有点酷,因为我们也可以抽象出通用的集合类型。

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))

It seems like the compiler infers the type as AnyRef. 似乎编译器将类型推断为AnyRef。 Defining it as AnyVal works. 将其定义为AnyVal即可。 AnyRef is the supertype of all objects, while AnyVal is the supertype of all primitives. AnyRef是所有对象的超类型,而AnyVal是所有基元的超类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM