2011-01-20 29 views
23

Tôi đang cố tạo một lớp Vector chung chung cho tất cả các kiểu số. nỗ lực ban đầu của tôi là viết một lớp học cho tất cả các loại như thế này:Làm cách nào để tạo một lớp chung cho tất cả các loại số?

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

từ scala hỗ trợ các chú thích chuyên ngành tôi có thể sử dụng để tạo ra cho tôi những lớp dành cho tất cả các loại số

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

nhưng tất cả mọi thứ Tôi tìm thấy như một loại siêu cho số là AnyVal, nhưng AnyVal không hỗ trợ + - * /. Vậy cách đúng đắn để làm điều này là gì, nhưng không hy sinh hiệu suất của các loại số không được mở hộp?

+0

Bạn có thể muốn xem xét câu hỏi này: http://stackoverflow.com/questions/4436936/scala-compiler-not-recognizing-a-view-bound/4437336#4437336 – Madoc

Trả lời

15

Bạn không thể. Không phải bây giờ. Có lẽ khi nào, và nếu, Numeric được chuyên môn hóa.

Giả sử bạn có được lớp đơn giản nhất có thể tham số:

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

Phương pháp + sẽ biên dịch thành một cái gì đó gần như thế này:

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

Đó là scalac -optimize -Xprint:jvm đầu ra. Bây giờ thậm chí có cả lớp con cho từng loại chuyên biệt, để bạn có thể khởi tạo Vector3 mà không cần đấm bốc, nhưng miễn là Numeric không chuyên biệt, bạn không thể đi xa hơn.

Vâng ... bạn có thể viết Numeric của riêng bạn và chuyên điều đó, nhưng, tại thời điểm đó, tôi không chắc chắn những gì bạn đang đạt được bằng cách làm cho lớp tham số ở vị trí đầu tiên.

6

Bạn có thể muốn sử dụng mô hình typeclass như đã mô tả ở đây: http://dcsobral.blogspot.com/2010/06/implicit-tricks-type-class-pattern.html

Hoặc, bạn có thể gián tiếp sử dụng bằng cách bằng cách sử dụng các đặc điểm Numeric http://www.scala-lang.org/api/current/scala/math/Numeric.html

+1

Thật buồn cười khi bài đăng trên blog được liên kết trong câu trả lời này được viết bởi người đã cung cấp câu trả lời toàn diện hơn nhưng thấp hơn cho cùng một câu hỏi này. :) –

+0

Có, tôi cũng nhận thấy 'dcsobral' trong liên kết blog và đã thấy câu trả lời của Daniel ở trên. – javadba

8

Câu trả lời ngắn gọn là: bạn không thể có được thực hiện đầy đủ . Hoặc ít nhất tôi đã không tìm thấy bất cứ điều gì cho hiệu suất đầy đủ. (Và tôi đã thử một lúc trong chính xác trường hợp sử dụng này; Tôi đã từ bỏ và viết một trình tạo mã thay thế, đặc biệt là vì bạn cũng không thể xử lý các kích thước vector khác nhau.)

Tôi rất vui mừng được hiển thị theo cách khác, nhưng cho đến nay tất cả mọi thứ tôi đã thử đã có một sự gia tăng nhỏ (30%) đến lớn (900%) trong thời gian chạy.


Chỉnh sửa: đây là bài kiểm tra hiển thị ý tôi.

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

và đầu ra:

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 
... 
+0

Bạn đã thử tạo lớp 'Numeric' của riêng mình chưa? –

+0

Không có trong 2.8.1. Trước đó trong 2.8 (cuối 2.8.0 RC, IIRC) có một số vấn đề mà tôi không còn nhớ rằng giữ hiệu suất ở mức độ hơi ấn tượng. Tôi đoán tôi nên thử lại. –

+0

Chỉ cần thử lại. Nó có vẻ ổn cho các hoạt động có thể thay đổi với Sun JVM, nhưng một khi bạn cần tạo các đối tượng mới, có một hình phạt ~ 2x. (Các JRockit JVM cho thấy cùng một xu hướng nhưng tất cả timings là 2-3x tồi tệ hơn, thường, và đôi khi chỉ có thể làm cho trường hợp công việc đặc biệt có thể thay đổi.) –

Các vấn đề liên quan