2011-01-20 12 views
23

我想創建一個通用於所有數字類型的Vector類。 我最初的嘗試是寫一個類的所有此類類型:如何爲所有數字類型製作通用類?

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

在Scala支持特殊註釋,我可以用它來生成我這些類所有數值類型

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

但一切我發現作爲數字的超級類型是AnyVal,但AnyVal不支持+ - * /。那麼做到這一點的正確方法是什麼,但又不會犧牲取消裝箱數字類型的性能?

+0

你可能想看看這個問題:http://stackoverflow.com/questions/4436936/scala-compiler-not-recognizing-a-view-bound/4437336#4437336 – Madoc 2011-01-20 23:57:14

回答

15

你不能。不是現在。可能何時,如果,Numeric得到專業化。

說你弄個最簡單的參數化類可能:

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

方法+將編譯成的東西大致是這樣的:

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

這是scalac -optimize -Xprint:jvm輸出。現在甚至有每種專門類型的子類,所以你可以在沒有裝箱的情況下初始化一個Vector3,但只要Numeric沒有專門化,你就不能走得更遠了。

呃...你可以自己編寫Numeric並專注於此,但在這一點上,我不確定你是通過首先將類參數化來獲得什麼。

6

你可能想使用類型類模式如下所述:http://dcsobral.blogspot.com/2010/06/implicit-tricks-type-class-pattern.html

或者,您也可以間接地通過使用數字特性使用由http://www.scala-lang.org/api/current/scala/math/Numeric.html

+1

有點有趣的是,在這個答案中鏈接的博客文章是由提供了一個更全面但得分較低的答案來解決這個問題的人寫的。 :) – 2011-01-21 06:09:44

+0

是的,我也注意到了博客鏈接中的`dcsobral`,並且已經在上面看到了丹尼爾的答案。 – javadba 2016-07-04 16:02:17

8

簡短的回答是:你不能得到充分的表現。或者至少我還沒有發現任何能夠提供全面表現的東西。 (並且我已經嘗試了這個用例,正好這個用例;我放棄並寫了一個代碼生成器來代替,特別是因爲你不能處理不同的矢量大小)

我很高興否則會顯示,但迄今爲止,我嘗試過的所有內容都有一小部分(30%)在運行時增加了很多(900%)。


編輯:這裏是一個測試,顯示我的意思。

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

和輸出:

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 
... 
相關問題