如何为所有数字类型创建一个通用类?

时间:2011-01-20 22:58:27

标签: scala polymorphism type-parameter

我正在尝试创建一个对所有数字类型都通用的Vector类。 我最初的尝试是为所有类型编写一个类,如下所示:

class Vector3f(val x:Float, val y:Float, val z:Float)

因为scala支持专门的注释,我可以使用它来为所有数字类型生成这些类

class Vector3[A <: What?](val x:A,val y:A, val z:A)

但我发现作为数字的超级类型的所有东西都是AnyVal,但AnyVal不支持+ - * /。那么这样做的正确方法是什么,但不会牺牲未装箱数字类型的性能?

3 个答案:

答案 0 :(得分:15)

你做不到。不是现在。也许何时,如果,Numeric变得专业化。

假设您获得了最简单的参数化类:

class Vector3[@specialized T](val x: T, val y: T, val z: T)(implicit num: Numeric[T]) {
    def +(other: Vector3[T]) = new Vector3(num.plus(x, other.x), num.plus(y, other.y), num.plus(z, other.z))
}

方法+将编译成大致类似的东西:

override <specialized> def +$mcD$sp(other: Vector3): Vector3 = new Vector3$mcD$sp(
  scala.Double.unbox(
    Vector3$mcD$sp.this.Vector3$$num.plus(
      scala.Double.box(Vector3$mcD$sp.this.x()), 
      scala.Double.box(other.x$mcD$sp()))),
  scala.Double.unbox(
    Vector3$mcD$sp.this.Vector3$$num.plus(
      scala.Double.box(Vector3$mcD$sp.this.y()),
      scala.Double.box(other.y$mcD$sp()))),
  scala.Double.unbox(
    Vector3$mcD$sp.this.Vector3$$num.plus(
      scala.Double.box(Vector3$mcD$sp.this.z()), 
      scala.Double.box(other.z$mcD$sp()))), 
  Vector3$mcD$sp.this.Vector3$$num);

那是scalac -optimize -Xprint:jvm输出。现在每个专用类型都有子类,因此您可以在没有装箱的情况下初始化Vector3,但只要Numeric不是专门的,就不能再进一步了。

嗯......你可以编写自己的Numeric并专注于此,但是,在那一点上,我不确定你是通过首先将类参数化来获得的。

答案 1 :(得分:8)

简短的回答是:你无法获得完整的表现。或者至少我没有找到任何可以提供完整性能的东西。 (我已经尝试了一段时间完全这个用例;我放弃并编写了一个代码生成器,特别是因为你无法处理不同的矢量大小。)

我很高兴能够以其他方式展示,但到目前为止,我所尝试过的所有内容都在运行时增加了很少(30%)到最大(900%)。


编辑:这是一个显示我的意思的测试。

object Specs {
  def ptime[T](f: => T): T = {
    val t0 = System.nanoTime
    val ans = f
    printf("Elapsed: %.3f s\n",(System.nanoTime-t0)*1e-9)
    ans
  }
  def lots[T](n: Int, f: => T): T = if (n>1) { f; lots(n-1,f) } else f

  sealed abstract class SpecNum[@specialized(Int,Double) T] {
    def plus(a: T, b: T): T
  }

  implicit object SpecInt extends SpecNum[Int] {
    def plus(a: Int, b: Int) = a + b
  }

  final class Vek[@specialized(Int,Double) T](val x: T, val y: T) {
    def +(v: Vek[T])(implicit ev: SpecNum[T]) = new Vek[T](ev.plus(x,v.x), ev.plus(y,v.y))
  }

  final class Uek[@specialized(Int,Double) T](var x: T, var y: T) {
    def +=(u: Uek[T])(implicit ev: SpecNum[T]) = { x = ev.plus(x,u.x); y = ev.plus(y,u.y); this }
  }

  final class Veq(val x: Int, val y: Int) {
    def +(v: Veq) = new Veq(x + v.x, y + v.y)
  }

  final class Ueq(var x: Int, var y: Int) {
    def +=(u: Ueq) = { x += u.x; y += u.y; this }
  }

  def main(args: Array[String]) {
    for (i <- 1 to 6) {
      ptime(lots(1000000,{val v = new Vek[Int](3,5); var u = new Vek[Int](0,0); var i=0; while (i<100) { u = (u+v); i += 1 }; u}))
      ptime(lots(1000000,{val v = new Veq(3,5); var u = new Veq(0,0); var i=0; while (i<100) { u = (u+v); i += 1 }; u}))
      ptime(lots(1000000,{val v = new Uek[Int](3,5); val u = new Uek[Int](0,0); var i=0; while (i<100) { u += v; i += 1 }; u}))
      ptime(lots(1000000,{val v = new Ueq(3,5); val u = new Ueq(0,0); var i=0; while (i<100) { u += v; i += 1 }; u}))
    }
  }
}

和输出:

Elapsed: 0.939 s
Elapsed: 0.535 s
Elapsed: 0.077 s
Elapsed: 0.075 s
Elapsed: 0.947 s
Elapsed: 0.352 s
Elapsed: 0.064 s
Elapsed: 0.063 s
Elapsed: 0.804 s
Elapsed: 0.360 s
Elapsed: 0.064 s
Elapsed: 0.062 s
Elapsed: 0.521 s  <- Immutable specialized with custom numeric
Elapsed: 0.364 s  <- Immutable primitive type
Elapsed: 0.065 s  <- Mutable specialized with custom numeric
Elapsed: 0.065 s  <- Mutable primitive type
...

答案 2 :(得分:6)

您可能希望使用此处所述的类型类模式:http://dcsobral.blogspot.com/2010/06/implicit-tricks-type-class-pattern.html

或者,您可以使用数字特征http://www.scala-lang.org/api/current/scala/math/Numeric.html

间接使用