我正在尝试创建一个对所有数字类型都通用的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不支持+ - * /。那么这样做的正确方法是什么,但不会牺牲未装箱数字类型的性能?
答案 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
间接使用