2016-06-05 9 views
10

부호없는 정수와 부호있는 정수 둘 다에 대해 번호 매기기 시스템을 설계하려고합니다. 이 두 유형 모두 Scala의 숫자 시스템에서 숫자를 나타내는 underlying 값을가집니다. 지금까지 가지고있는 타입 계층 구조가 있습니다.부호없는 및 부호있는 정수로 번호 매기기 시스템 설계

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 
} 

그래서 컴파일러는 underlying 아이들 모두에 정의되어 있음을 시행 할 수있는 특성 Numberunderlying을 정의하고 싶습니다. 그러나 underlying의 유형은 각 특성에 따라 다릅니다. 각 유형에 대해 가능한 가장 작은 유형을 유지하려고합니다. 예를 들어, UInt32UInt64BigInt으로 저장되어야하는 반면 Scala에서는 long으로 저장 될 수 있습니다.

가장 효과적인 방법은 무엇입니까?

답변

3

부모 특성에 type을 선언하고이를 자막에서 무시할 수 있습니다.

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 
} 

그냥 명확하지 않다 경우 경로 의존적 유형의 사용을 보여주기 위해 :

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

반환 형식이 올바른 얻으려면을, 나는 또 다른 type이 필요할 수 있습니다 생각합니다.

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

그래서이 접근법을 생각해 보았습니다. Ocaml은 모듈에서 항상 이것을 사용합니다. '+','-','*'와 같은 것에 서명을 어떻게 구현합니까? 모든 함수에 대해'A' 형을 반환하겠습니까? 결국 최종 사용자가 실제로 기본 유형에 액세스하는 방법에 대한 의문이 생깁니다. 일부 함수 (a : A) : <기본 유형은 여기>? –

+0

또한 공개 범위에서 '기반'을 갖는 것이 유형 캡슐화를 방해합니까? 나는 내가 추측하는 울타리에있다 ... –

+0

좀 더 생각으로 편집 됨. 기본 유형/값을 노출할지 여부를 결정해야합니다. 상속을 사용하는 대신'Number' 타입 클래스를 만드는 것도 고려해 볼만합니다. –

0

가장 효율적인 방법은 원시 형식으로 원시 숫자 (Int Double ...)를 저장하는 것입니다.

unsignedness는 런타임에 지워지는 Type 매개 변수에 저장해야합니다. 스칼라는 간단한 케이스 클래스가 AnyVal을 확장하도록 할 때 이것을 수행한다.

다음 코드는 Ints, Longs, Doubles 및 Bigint에 대해이 작업을 수행합니다. 서명되지 않은 상태로 몇 가지 분류를 추가하고 서명되지 않은 상태로 이름을 바꿨습니다.

또한 분류가 모두 유형 시스템에서 완료되었으므로 많은 오버로드 된 + 및 * 기능을 제공 할 필요가 없습니다. 이렇게하면 모든 숫자 유형에 대해이를 구현할 때 공간이 절약됩니다.

다양한 유형간에 브리징 할 때 약간의 작업이 남아 있습니다. 나는 이것을 나중에 보게 될 것이다.

분류 특성 :

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

지능 래퍼 :

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])={}; 
} 

긴 래퍼 :

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])={}; 
} 

두 래퍼 :

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 래퍼 :

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])={}; 
} 

시험 구 :

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

시. 실제로 바이트 코드를 보지 못했지만, 문서는 인라인되어야하고 원시 형식으로 컴파일되어야한다고 sugests.

나는이 사랑이 너무 좋아.

관련 문제