2016-06-05 7 views
10

मैं दोनों हस्ताक्षरित पूर्णांक और हस्ताक्षरित पूर्णांक के चारों ओर एक संख्या प्रणाली को डिजाइन करने की कोशिश कर रहा हूं। इन दोनों प्रकारों में 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 
} 

मैं विशेषता Number में underlying परिभाषित करने के लिए तो संकलक लागू कर सकते हैं कि underlying बच्चों के सभी में परिभाषित किया गया है चाहते हैं। हालांकि, प्रत्येक विशेषता के लिए underlying के प्रकार अलग-अलग होते हैं - मैं प्रत्येक प्रकार के लिए सबसे छोटा संभव प्रकार रखना चाहता हूं। उदाहरण के लिए, UInt32 को स्कैला में long के रूप में संग्रहीत किया जा सकता है, जबकि UInt64 को BigInt के रूप में संग्रहीत करने की आवश्यकता है।

ऐसा करने का सबसे प्रभावी तरीका क्या है?

उत्तर

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

तो मैंने इस दृष्टिकोण को लेने के बारे में सोचा - ओकम्ल अपने मॉड्यूल में हर समय इसका उपयोग करता है। '+', '-',' * 'जैसी चीज़ों पर हस्ताक्षर कैसे कार्यान्वित करेंगे? क्या आपके पास उन सभी कार्यों के लिए 'ए' प्रकार वापस किया जाएगा? इससे सवाल उठता है कि अंत उपयोगकर्ता वास्तव में अंतर्निहित प्रकार का उपयोग कैसे करता है? कुछ फ़ंक्शन (ए: ए): <अंतर्निहित प्रकार यहां>? –

+0

क्या सार्वजनिक दायरे में 'अंतर्निहित' होने से टाइप इंकैप्यूलेशन कमजोर पड़ता है? मैं बाड़ पर हूं मुझे लगता है ... –

+0

कुछ और विचारों के साथ संपादित किया गया। मुझे लगता है कि आपको यह तय करना होगा कि क्या आप अंतर्निहित प्रकार/मूल्य का खुलासा करना चाहते हैं या नहीं। मुझे लगता है कि आप विरासत का उपयोग करने के बजाय 'संख्या' प्रकार वर्ग बनाने पर भी विचार कर सकते हैं। –

0

सबसे प्रभावी तरीका कच्चे प्रकार के रूप में आदिम संख्या (Int Double ...) को स्टोर करना सबसे प्रभावी तरीका है।

हस्ताक्षर को टाइप पैरामीटर में संग्रहीत किया जाना चाहिए, जिसे रनटाइम पर मिटा दिया जाएगा। स्कैला ऐसा करता है जब आप सरल केस कक्षाओं को AnyVal का विस्तार करते हैं।

फोल्टिंग कोड इंट्स, लॉन्ग्स, डबल्स और बिगिंट के लिए यह करता है। मैंने हस्ताक्षर किए जाने के लिए कुछ वर्गीकरण जोड़े और बिना हस्ताक्षर किए गए सकारात्मक का नाम बदल दिया।

चूंकि वर्गीकरण सभी प्रकार के सिस्टम में किया जाता है, इसलिए हमें कई अधिभारित + - और * फ़ंक्शंस प्रदान करने की आवश्यकता नहीं होती है। यह सभी संख्या प्रकारों के लिए इसे लागू करने का प्रयास करते समय अंतरिक्ष को बचाएगा।

विभिन्न प्रकारों के बीच ब्रिजिंग करते समय अभी भी कुछ किया जाना बाकी है। मैं बाद में इसे देख लूंगा।

वर्गीकरण लक्षण:

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

Ps। वास्तव में बाइटकोड को नहीं देखा है, लेकिन दस्तावेज़ों ने यह सुझाव दिया है कि इसे रेखांकित किया जाना चाहिए और प्राइमेटिव्स को संकलित किया जाना चाहिए।

मुझे बस इस लेन-देन से प्यार है।

संबंधित मुद्दे