2011-01-23 24 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秒