가장 효율적인 방법은 원시 형식으로 원시 숫자 (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.
나는이 사랑이 너무 좋아.
그래서이 접근법을 생각해 보았습니다. Ocaml은 모듈에서 항상 이것을 사용합니다. '+','-','*'와 같은 것에 서명을 어떻게 구현합니까? 모든 함수에 대해'A' 형을 반환하겠습니까? 결국 최종 사용자가 실제로 기본 유형에 액세스하는 방법에 대한 의문이 생깁니다. 일부 함수 (a : A) : <기본 유형은 여기>? –
또한 공개 범위에서 '기반'을 갖는 것이 유형 캡슐화를 방해합니까? 나는 내가 추측하는 울타리에있다 ... –
좀 더 생각으로 편집 됨. 기본 유형/값을 노출할지 여부를 결정해야합니다. 상속을 사용하는 대신'Number' 타입 클래스를 만드는 것도 고려해 볼만합니다. –