2016-06-05 16 views
10

Tôi đang cố thiết kế một hệ thống đánh số quanh cả số nguyên chưa ký và số nguyên đã ký. Cả hai loại này đều có giá trị underlying đại diện cho số trong hệ thống số của Scala. Đây là hệ thống phân cấp kiểu mà tôi có cho đến nay.Thiết kế hệ thống đánh số với các ký tự chưa ký và đã ký

sealed trait Number { 
    def + (num : Number) : Number = ??? 
    def - (num : Number) : Number = ??? 
    def * (num : Number) : Number = ??? 
} 

sealed trait SignedNumber extends Number 

sealed trait UnsignedNumber extends Number 

sealed trait UInt32 extends UnsignedNumber { 
    def underlying : Long 
} 

sealed trait UInt64 extends UnsignedNumber { 
    def underlying : BigInt 
} 

sealed trait Int32 extends SignedNumber { 
    def underlying : Int 
} 

sealed trait Int64 extends SignedNumber { 
    def underlying : Long 
} 

Tôi muốn xác định underlying trong đặc điểm Number quá trình biên dịch có thể thực thi mà underlying được định nghĩa trong tất cả các trẻ em. Tuy nhiên, các loại cho underlying khác nhau cho mỗi đặc điểm - tôi muốn giữ loại nhỏ nhất có thể cho mỗi loại. Ví dụ: UInt32 có thể được lưu trữ dưới dạng long trong Scala, trong khi UInt64 cần được lưu trữ dưới dạng BigInt.

Cách hiệu quả nhất để thực hiện việc này là gì?

Trả lời

3

Bạn có thể khai báo type trong đặc điểm gốc và ghi đè nó trong phần phụ.

sealed trait Number { 
    type A 
    def underlying: A 
    def + (num : Number) : Number = ??? 
    def - (num : Number) : Number = ??? 
    def * (num : Number) : Number = ??? 
} 

sealed trait SignedNumber extends Number 

sealed trait UnsignedNumber extends Number 

sealed trait UInt32 extends UnsignedNumber { 
    override type A = Long 
} 

sealed trait UInt64 extends UnsignedNumber { 
    override type A = BigInt 
} 

sealed trait Int32 extends SignedNumber { 
    override type A = Int 
} 

sealed trait Int64 extends SignedNumber { 
    override type A = Long 
} 

Một ví dụ chỉ để hiển thị sử dụng các loại đường phụ thuộc trong trường hợp đó không phải là rõ ràng:

def getUnderlying(x: Number): x.A = x.underlying 

Để có được kiểu trả về đúng, tôi nghĩ type khác có thể được yêu cầu.

sealed trait Number { 
    type A 
    type B 
    def underlying: A 
    def +(that: B): B 
} 

sealed trait UInt32 extends Number { x => 
    override type A = Long 
    override type B = UInt32 
    override def +(y: B): B = new UInt32 { 
    // todo - naive implementation, doesn't check overflow 
    override val underlying = x.underlying + y.underlying 
    } 
} 

def main(args: Array[String]) { 
    print((
    new UInt32 { def underlying = 3 } + 
    new UInt32 { def underlying = 4 } 
).underlying) 
} 
+0

Vì vậy, tôi đã nghĩ về cách tiếp cận này - Ocaml sử dụng tất cả thời gian trong các mô-đun của họ. Làm thế nào để thực hiện chữ ký trên những thứ như '+', '-',' * '? Bạn có kiểu 'A' được trả về cho tất cả các hàm đó không? Điều đó dẫn đến câu hỏi về cách người dùng cuối thực sự truy cập vào loại cơ bản như thế nào? Một số hàm (a: A): ? –

+0

Cũng có 'cơ bản' trong một phạm vi đóng gói loại trừ phạm vi công khai? Tôi đang ở hàng rào mà tôi đoán ... –

+0

Chỉnh sửa với một số suy nghĩ khác. Tôi đoán bạn sẽ phải quyết định xem bạn muốn loại/giá trị cơ bản được tiếp xúc hay không. Tôi cho rằng bạn cũng có thể cân nhắc tạo một lớp kiểu 'Số' thay vì sử dụng thừa kế. –

0

Cách hiệu quả nhất là lưu trữ số nguyên thủy (Int Double ...) làm loại thô.

Không được lưu trữ unsignness trong thông số Loại, sẽ bị xóa khi chạy. Scala thực hiện điều này khi bạn để các trường hợp đơn giản mở rộng AnyVal.

Mã folowing thực hiện điều này cho Ints, Longs, Doubles và Bigint. Tôi đã thêm một vài phân loại trong adition để unsigned và đổi tên unsigned thành tích cực.

Cũng vì việc phân loại được thực hiện trong hệ thống kiểu, chúng tôi không cần cung cấp quá nhiều hàm + - và * quá tải. Điều này sẽ tiết kiệm không gian khi cố gắng thực hiện điều này cho tất cả các loại số.

Vẫn còn một chút việc cần thực hiện khi kết nối giữa các loại khác nhau. Tôi sẽ xem xét điều này sau.

Các đặc điểm phân loại:

sealed trait SignTag{ 
    type SubTag <:SignTag; 
    type AddTag <:SignTag; 
    type MultTag<:SignTag; 
} 

sealed trait Signed extends SignTag{ 
    type SubTag=Signed; 
    type AddTag=Signed; 
    type MultTag=Signed; 
} 

sealed trait Positive extends SignTag{ 
    type SubTag=Signed; 
    type AddTag=Negative; 
    type MultTag=Negative; 
} 

sealed trait Negative extends SignTag{ 
    type SubTag=Signed; 
    type AddTag=Negative; 
    type MultTag=Positive; 
} 

sealed trait Zero extends SignTag{ 
    type SubTag=Zero; 
    type AddTag=Zero; 
    type MultTag=Zero; 
} 

Int wrapper:

object SInt { 
    @inline 
    implicit def toSigned[T <: SignTag](int:SInt[T]):SInt[Signed]=int.asInstanceOf[SInt[Signed]]; 

    @inline implicit def toLong[T <: SignTag](int:SInt[T]):SLong[T]=SLong(int.underlying); 
    @inline implicit def toDouble[T <: SignTag](int:SInt[T]):SDouble[T]=SDouble(int.underlying); 
    @inline implicit def toBig[T <: SignTag](int:SInt[T]):SBigInt[T]=SBigInt(int.underlying); 
} 

case class SInt[T <: SignTag](val underlying:Int) extends AnyVal{ 
    def -(second: SInt[_ <: T#InTag]):SInt[T#SubTag]=new SInt[T#SubTag](underlying - second.underlying); 

    def +(second: SInt[_ <: T#InTag]):SInt[T#AddTag]=new SInt[T#AddTag](underlying + second.underlying); 

    def *(second: SInt[_ <: T#InTag]):SInt[T#MultTag]=new SInt[T#MultTag](underlying * second.underlying); 

    def assertSameType(other:SInt[T])={}; 
} 

dài wrapper:

object SLong { 

    @inline 
    implicit def toSigned[T <: SignTag](int:SLong[T]):SLong[Signed]=int.asInstanceOf[SLong[Signed]]; 

    @inline implicit def toDouble[T <: SignTag](int:SLong[T]):SDouble[T]=SDouble(int.underlying); 
    @inline implicit def toBig[T <: SignTag](int:SLong[T]):SBigInt[T]=SBigInt(int.underlying); 
} 

case class SLong[T <: SignTag](val underlying:Long) extends AnyVal{ 
    def -(second: SLong[_ <: T#InTag]):SLong[T#SubTag]=new SLong[T#SubTag](underlying - second.underlying); 

    def +(second: SLong[_ <: T#InTag]):SLong[T#AddTag]=new SLong[T#AddTag](underlying + second.underlying); 

    def *(second: SLong[_ <: T#InTag]):SLong[T#MultTag]=new SLong[T#MultTag](underlying * second.underlying); 

    def assertSameType(other:SLong[T])={}; 
} 

đúp wrapper:

object SDouble { 
    @inline 
    implicit def toSigned[T <: SignTag](int:SDouble[T]):SDouble[Signed]=int.asInstanceOf[SDouble[Signed]]; 
} 

case class SDouble[T <: SignTag](val underlying:Double) extends AnyVal{ 
    def -(second: SDouble[_ <: T#InTag]):SDouble[T#SubTag]=new SDouble[T#SubTag](underlying - second.underlying); 

    def +(second: SDouble[_ <: T#InTag]):SDouble[T#AddTag]=new SDouble[T#AddTag](underlying + second.underlying); 

    def *(second: SDouble[_ <: T#InTag]):SDouble[T#MultTag]=new SDouble[T#MultTag](underlying * second.underlying); 

    def assertSameType(other:SDouble[T])={}; 
} 

bigint wrapper:

object SBigInt { 
    @inline 
    implicit def toSigned[T <: SignTag](int:SLong[T]):SLong[Signed]=int.asInstanceOf[SLong[Signed]]; 

    @inline 
    implicit def toDouble[T <: SignTag](int:SBigInt[T]):SDouble[T]=SDouble(int.underlying.toDouble); 
} 

case class SBigInt[T <: SignTag](val underlying:BigInt) extends AnyVal{ 
    def -(second: SBigInt[_ <: T#InTag]):SBigInt[T#SubTag]=new SBigInt[T#SubTag](underlying - second.underlying); 

    def +(second: SBigInt[_ <: T#InTag]):SBigInt[T#AddTag]=new SBigInt[T#AddTag](underlying + second.underlying); 

    def *(second: SBigInt[_ <: T#InTag]):SBigInt[T#MultTag]=new SBigInt[T#MultTag](underlying * second.underlying); 

    def assertSameType(other:SBigInt[T])={}; 
} 

Kiểm tra cú pháp:

class CompileToTest { 
    val signed=new SInt[Signed](5); 
    val positive=new SInt[Positive](5); 
    val negative=new SInt[Negative](-5); 
    val zero=new SInt[Zero](0); 

    (signed + signed).assertSameType(signed); 
    (negative + signed).assertSameType(signed); 
    (positive - positive).assertSameType(signed); 
    (positive * negative).assertSameType(signed); 
    (zero + zero).assertSameType(zero); 

    val positiveDouble=SDouble[Positive](4.4) 
    val negativeDouble=SDouble[Negative](-4.4) 
    val signedDouble=SDouble[Signed](-4.4) 

    (positiveDouble * negativeDouble).assertSameType(signedDouble); 
} 

Ps. Đã không thực sự nhìn vào bytecode, nhưng các tài liệu sugests rằng điều này nên được inlined và biên dịch xuống nguyên thủy.

Tôi chỉ thích lanuage này.

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