2011-01-23 22 views
5

我想有一个一般的矢量抽象类/性状,指定某些方法中,例如:适当类层次矢量

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

    def dot(v:Vec):Double 
    def norm:Double 
} 

我想有Vec2DVec3D延伸Vec

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

但是,我怎么可以,使Vec2D只能被添加到其他Vec2D而不是Vec3D

现在我只是实施Vec2DVec3D没有一个共同的Vec祖先,但这是繁琐的重复代码。我必须实现所有依赖于这些类的几何类(例如,Triangle,Polygon,Mesh,...)两次,一次为Vec2D,再次为Vec3D

我看到了java的实现:javax.vecmath.Vector2djavax.vecmath.Vector3d没有共同的祖先。这是什么原因?有没有办法在scala中克服它?

回答

5

由于requested,设计基本特征的最有用的方法既涉及CRTPself-type annotation

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 
} 

没有自型,这是不可能调用this.dot(this)作为dot期望一个T;因此我们需要通过注释来执行它。

在另一方面,没有CRTP,我们就不能调用norm(this - v)-回报T,因此,我们需要确保我们的类型T有这种方法,例如声明TVec[T]

4

我不确定适当的Scala语法,但您可以实现CRTP,即通过泛型参数定义实际类型。

trait Vec[V <: Vec[V]] { 
    def +(v:V):V 
    ... 
} 

class Vec2D extends Vec[Vec2D] { } 
class Vec3D extends Vec[Vec3D] { } 

class Polygon[V <: Vec[V]] { 
    ... 
} 
+0

点。语法是正确的,一切!我猜java不支持这个(否则与javax.vecmath有什么关系)? – dsg 2011-01-23 12:13:54

+0

其实,我猜java确实支持这个:http://stackoverflow.com/questions/2382915/what-does-this-java-generics-paradigm-do-and-what-is-it-cal-叫 – dsg 2011-01-23 12:56:00

7

您可以使用自己的类型:

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 */ } 

但是,如果这两种方案都非常相似,你也可以尝试到抽象的过度尺寸。

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

当然,这取决于您想要如何表示数据,以及您是否想要可变或不可变实例。而对于“真实世界”的应用程序,你应该考虑http://code.google.com/p/simplex3d/

2

在JVM上使用CRTP模式的共同祖先存在很大的问题。当您使用不同的实现执行相同的抽象代码时,JVM将会优化代码(不内联+虚拟调用)。如果您仅使用Vec3D进行测试,则不会注意到这一点,但如果使用Vec2D和Vec3D进行测试,则会发现性能大幅下降。此外,Escape Analysis不能应用于去优化代码(无标量替换,不消除新实例)。 缺少这些优化会使您的程序减慢3倍(非常圆润的猜测取决于您的代码)。

尝试一些运行约10秒的基准。在Vec2D,Vec3D,Vec2D,Vec3D的同样的运行测试中。你会看到这样的图案:

  • Vec2D到10秒
  • Vec3D〜30秒
  • Vec2D〜30秒
  • Vec3D〜30秒