vectores unitarios tema segundo fisica cuadrante coordenadas componentes completa clase oop scala architecture hierarchy

oop - unitarios - jerarquía de clases adecuada para vectores 2D y 3D



vectores en 3d fisica (4)

No estoy seguro de la sintaxis correcta de Scala, pero puede implementar el CRTP , es decir, definir el tipo real a través de un parámetro genérico.

trait Vec[V <: Vec[V]] { def +(v:V):V ... } class Vec2D extends Vec[Vec2D] { } class Vec3D extends Vec[Vec3D] { } class Polygon[V <: Vec[V]] { ... }

Quiero tener una clase / rasgo abstracto de vector general que especifique ciertos métodos, por ejemplo:

trait Vec { def +(v:Vec):Vec def *(d:Double):Vec def dot(v:Vec):Double def norm:Double }

Quiero que Vec2D y Vec3D extiendan Vec :

class Vec2D extends Vec { /* implementation */ } class Vec3D extends Vec { /* implementation */ }

Pero, ¿cómo puedo, por ejemplo, hacer que Vec2D solo se pueda agregar a otro Vec2D y no a Vec3D ?

En este momento solo estoy implementando Vec2D y Vec3D sin un antecesor Vec común, pero esto se está Vec3D tedioso con código duplicado. Tengo que implementar todas mis clases de geometría que dependen de estas clases (por ejemplo, Triangle , Polygon , Mesh , ...) dos veces, una para Vec2D y otra para Vec3D .

Veo las implementaciones de Java: javax.vecmath.Vector2d y javax.vecmath.Vector3d no tienen un antecesor común. ¿Cuál es el motivo de esto? ¿Hay alguna manera de superarlo en Scala?


Puedes usar self-types:

trait Vec[T] { self:T => def +(v:T):T def *(d:Double):T def dot(v:T):Double def norm:Double } class Vec2D extends Vec[Vec2D] { /* implementation */ } class Vec3D extends Vec[Vec3D] { /* implementation */ }

Pero si ambas implementaciones son muy similares, también podría intentar abstraerse sobre la Dimensión.

sealed trait Dimension case object Dim2D extends Dimension case object Dim3D extends Dimension sealed abstract class Vec[D <: Dimension](val data: Array[Double]) { def +(v:Vec[D]):Vec[D] = ... def *(d:Double):Vec[D] = ... def dot(v:Vec[D]):Double = ... def norm:Double = math.sqrt(data.map(x => x*x).sum) } class Vec2D(x:Double, y:Double) extends Vec[Dim2D.type](Array(x,y)) class Vec3D(x:Double, y:Double, z:Double) extends Vec[Dim3D.type](Array(x,y,z))

Por supuesto, depende de cómo quiera representar los datos y si quiere tener instancias mutables o inmutables. Y para aplicaciones del "mundo real" debería considerar http://code.google.com/p/simplex3d/


Hay un gran problema con tener un ancestro común con el patrón CRTP en JVM. Cuando ejecuta el mismo código abstracto con diferentes implementaciones, JVM desoptimizará el código (sin enlining + llamadas virtuales). No lo notarás si solo pruebas con Vec3D, pero si pruebas tanto con Vec2D como con Vec3D verás una gran caída en el rendimiento. Además, el Análisis de escape no se puede aplicar al código de optimización (sin reemplazo escalar, sin eliminación de nuevas instancias). La falta de estas optimizaciones reducirá la velocidad de su programa en un factor de 3 (una estimación muy completa que depende de su código).

Pruebe algunos puntos de referencia que se ejecutan durante aproximadamente 10 segundos. En la misma prueba de ejecución con Vec2D, luego Vec3D, luego Vec2D, luego Vec3D nuevamente. Verás este patrón:

  • Vec2D ~ 10 segundos
  • Vec3D ~ 30 segundos
  • Vec2D ~ 30 segundos
  • Vec3D ~ 30 segundos

Tal como se solicitó , la forma más útil de diseñar el rasgo base implica tanto la anotación CRTP como la anotación de tipo propio .

trait Vec[T <: Vec[T]] { this: T => def -(v: T): T def *(d: Double): T def dot(v: T): Double def norm: Double = math.sqrt(this dot this) def dist(v: T) = (this - v).norm }

Sin el tipo propio, no es posible llamar this.dot(this) como dot espera una T ; por lo tanto, debemos aplicarlo con la anotación.

Por otro lado, sin CRTP, no llamaremos a norm (this - v) as - devuelve una T y, por lo tanto, debemos asegurarnos de que nuestro tipo T tiene este método, por ejemplo, declarar que T es un Vec[T]