scala polymorphism type-parameter

scala - ¿Cómo hago una clase genérica para todos los tipos numéricos?



scala annotations (3)

La respuesta corta es: no se puede obtener un rendimiento completo. O al menos no he encontrado nada que dé pleno rendimiento. (Y lo he intentado durante un tiempo exactamente en este caso de uso; me di por vencido y escribí un generador de código, especialmente porque tampoco puedes manejar diferentes tamaños de vectores de forma genérica).

Me encantaría que me mostraran lo contrario, pero hasta ahora todo lo que he intentado ha tenido un aumento pequeño (30%) a vasto (900%) en el tiempo de ejecución.

Edición: aquí hay una prueba que muestra lo que quiero decir.

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

y la salida:

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

Estoy intentando crear una clase Vector que es genérica para todos los tipos numéricos. mi intento original fue escribir una clase para todos los tipos como este:

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

ya que scala admite la anotación especializada, podría usar esto para generarme estas clases para todos los tipos numéricos

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

pero todo lo que encontré como un súper tipo para números fue AnyVal, pero AnyVal no admite + - * /. Entonces, ¿cuál es la forma correcta de hacer esto, pero sin sacrificar el rendimiento de los tipos de números sin caja?



Usted no puede No ahora. Tal vez cuando, y si, Numeric se especializa.

Digamos que obtienes la clase parametrizada más simple posible:

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

El método + se compilará en algo más o menos así:

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

Eso es scalac -optimize -Xprint:jvm output. Ahora hay incluso subclases para cada tipo especializado, de modo que puede inicializar un Vector3 sin boxeo, pero mientras Numeric no esté especializado, no puede ir más lejos.

Bueno ... puedes escribir tu propio Numeric y especializar eso, pero, en ese momento, no estoy seguro de lo que estás ganando al hacer que la clase esté parametrizada en primer lugar.