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

Arn*_*rne 24 polymorphism scala type-parameter

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

class Vector3f(val x:Float, val y:Float, val z:Float)
Run Code Online (Sandbox Code Playgroud)

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

class Vector3[A <: What?](val x:A,val y:A, val z:A)
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*ral 16

你不能.不是现在.也许何时,如果,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))
}
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

这是scalac -optimize -Xprint:jvm输出.现在每个专用类型都有子类,所以你可以初始化Vector3没有拳击,但只要Numeric不专业,你就不能再进一步了.

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


Rex*_*err 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}))
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

和输出:

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
...
Run Code Online (Sandbox Code Playgroud)


Mit*_*ins 6

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

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