¿Cómo se especifica un tipo con destino a flotado y doble en un tipo genérico en Scala?
-
27-09-2019 - |
Pregunta
Estoy escribiendo algunas simples clases de vectores y matrices. Son similares a esto:
// Vector with Floats
case class Vector3f(x: Float, y: Float, z: Float) {
def +(v: Vector3f) = Vector3f(x + v.x, y + v.y, z + v.z)
}
// Vector with Doubles
case class Vector3d(x: Double, y: Double, z: Double) {
def +(v: Vector3d) = Vector3d(x + v.x, y + v.y, z + v.z)
}
Si voy con otros métodos y clases como Point3f / d, Vector4f / d, Matrix3f / d, Matrix4f / d ... esto va a ser un montón de trabajo. Uff ... Así que pensé genéricos podría posible ayuda aquí y redundancia Sacar de mi base de código. Pensé en algo como esto:
// first I define a generic Vector class
case class Vector3[@specialized(Float, Double) T](x: T, y: T, z: T) {
def +(v: Vector3[T]) = new Vector3[T](x + v.x, y + v.y, z + v.z)
}
// than I use some type aliases to hide the generic nature
type Vector3f = Vector3[Float]
type Vector3d = Vector3[Double]
La idea es que el compilador genera Scala clases especializadas para Vector3 [Flotante] y Vector3 [doble] similar a una plantilla de C ++ haría. Por desgracia tengo que poner algún tipo con destino en el parámetro de tipo [T] de la clase Vector3 de tal manera que el operador + se define en T. Mi pregunta: ¿Cómo puedo escribir Vector3 [Flotante] que tiene las mismas características de rendimiento como Vector3f? Contexto:. Me gustaría utilizar las clases Vector3f / Vector3D en código de detección de colisiones ... lo que el rendimiento importa para mí
Solución
El uso de un contexto ligado fraccional:
case class Vector3[@specialized(Float, Double) T : Fractional](x: T, y: T, z: T) { ...
entonces dentro del cuerpo de la clase, obtener una instancia de los operadores aritméticos:
val fractOps = implicitly[Fractional[T]]
Por último importar sus miembros en el ámbito de la clase:
import fractOps._
A partir de entonces se puede escribir operaciones infijas ordinarias en valores de tipo T se utiliza dentro de la clase. Por desgracia, tendrá que utilizar fractOps.div(a, b)
en lugar de a / b
para la división.