2011-01-20 11 views
23

Estoy tratando de crear una clase vectorial que sea genérica para todos los tipos numéricos. mi intento original era la de escribir una clase para todos los tipos como este:¿Cómo puedo hacer una clase genérica para todos los tipos numéricos?

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

desde Scala soporta la anotación especializada que podría utilizar esto para mí generar estas clases para todos los tipos numéricos

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

pero todo Encontré como un tipo estupendo para los números AnyVal, pero AnyVal no admite + - * /. Entonces, ¿cuál es la forma correcta de hacerlo, pero sin sacrificar el rendimiento de los tipos de números sin caja?

+0

Es posible que desee echar un vistazo a esta pregunta: http://stackoverflow.com/questions/4436936/scala-compiler-not-recognizing-a-view-bound/4437336#4437336 – Madoc

Respuesta

15

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

que usted recibe la clase más simple posible parametrizar:

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 + 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 salida. Ahora incluso hay 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 allá.

Bueno ... puede escribir su propio Numeric y especializarlo, pero, en ese punto, no estoy seguro de lo que está ganando al hacer la clase parametrizada en primer lugar.

6

Es posible que desee utilizar el patrón de clase de tipos como se describe aquí: http://dcsobral.blogspot.com/2010/06/implicit-tricks-type-class-pattern.html

O bien, puede utilizar indirectamente mediante el uso de la característica numérica http://www.scala-lang.org/api/current/scala/math/Numeric.html

+1

Es un poco gracioso que el La publicación de blog vinculada en esta respuesta fue escrita por el tipo que proporcionó una respuesta más completa pero con una puntuación más baja a esta misma pregunta. :) –

+0

Sí, también noté el 'dcsobral' en el enlace del blog y ya había visto la respuesta de Daniel anterior. – javadba

8

La respuesta corta es: no se puede obtener un rendimiento completo . O al menos no he encontrado nada que ofrezca un rendimiento completo. (Y he tratado por un tiempo en exactamente este caso de uso; me di por vencido y escribió un generador de código en su lugar, sobre todo porque no se puede manejar diferentes tamaños vector genéricamente tampoco.)

Estaría encantada de se muestre lo contrario, pero hasta ahora todo lo que he probado ha tenido un aumento pequeño (30%) a gran (900%) en el tiempo de ejecución.


Editar: esta es una prueba que indica a qué me refiero.

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

¿Has probado hacer tu propia clase especializada 'numérica'? –

+0

No en 2.8.1. Anteriormente en 2.8 (tarde 2.8.0 RC, IIRC) hubo algún problema que ya no recuerdo que mantuvo el rendimiento en niveles algo poco impresionantes. Creo que debería intentarlo de nuevo. –

+0

Acabo de intentar nuevamente. Parece correcto para operaciones mutables con Sun JVM, pero una vez que necesita crear nuevos objetos, hay una penalización de ~ 2x. (JRockit JVM muestra la misma tendencia, pero todos los tiempos son 2-3 veces peores, por lo general, y solo en ocasiones puede hacer que el caso especializado mutable funcione.) –

Cuestiones relacionadas