2014-10-20 10 views
6

मान लीजिए कि आपके पास एक अच्छी अपूर्व परिभाषा है और आप इसे हास्केल में डेटा प्रकार के रूप में परिभाषित करना चाहते हैं। हालांकि, आपकी अपरिवर्तनीय परिभाषा इस तरह के रूप में (कई अपरिवर्तनीय परिभाषाएं हैं) है कि उत्पन्न नियमों को उनके 'प्रीमिसीज़' की एक निश्चित संरचना की आवश्यकता होती है। , डेटा प्रकारों को नियंत्रित करना

    • अगर x एक और भी पूर्णांक, तो T x एक हथियार है है अगर x, एक विषम पूर्णांक है तो S x एक हथियार है: उदाहरण के लिए, मान लीजिए हम निम्नलिखित परिभाषा है।

    मैं इस को परिभाषित करने के हास्केल में डेटा प्रकार (एक) के रूप में चाहते हैं, मैं

    data Weapon = T Int | S Int 
    

    जाहिर की तरह कुछ लिखते थे, इस के रूप में आप अब T 5 और S 4 उत्पन्न कर सकते हैं काम नहीं करेगा, के लिए उदाहरण। क्या रचनाकार तर्कों पर प्रतिबंधों को पारित करने का कोई प्राकृतिक तरीका है, ताकि मैं उपरोक्त कोड के समान कुछ लिख सकूं जो सही परिभाषा देगी? , उपयोगकर्ताओं को स्पष्ट रूप से T और S बनाने के लिए सक्षम नहीं होंगे जब YourModule आयात करने

    module YourModule (Weapon, smartConstruct) where 
    
    data Weapon = T Int | S Int 
    
    smartConstruct :: Int -> Weapon 
    smartConstruct x 
        | even x  = T x 
        | otherwise = S x 
    

    अब, लेकिन केवल के साथ:

  • +2

    स्मार्ट कन्स्ट्रक्टर, ज्यादातर।'टी' और' एस' का उपयोग करके सीधे फोर्बिड करें, और 'न्यूटी' और' नए एस 'फ़ंक्शंस बनाएं जो पारित संख्याओं को सत्यापित करते हैं। –

    उत्तर

    9

    आपका सबसे अच्छा शॉट स्पष्ट T और S निर्यात करने के लिए है, लेकिन अनुमति देने के लिए एक कस्टम निर्माता नहीं है आपका smartConstruct फ़ंक्शन।

    10

    यह थोड़ा अन-हास्कली है, लेकिन उदाहरण में अधिक मूर्खतापूर्ण है। आगाडा: अपने प्रतिनिधित्व की व्याख्या को बदलें ताकि इसे निर्माण द्वारा सही किया जा सके।

    इस मामले में, ध्यान दें कि n :: Int, तो even (2 * n) और odd (2 * n + 1)। अगर हम बहुत बड़े Int एस के मामले को दूर करते हैं, तो हम कह सकते हैं कि Int एस और Int एस के बीच भी एक विभाजन है; और अजीब Int एस और Int एस के बीच एक और एक।

    तो इस का उपयोग करते हुए, तो आप इस प्रतिनिधित्व कर सकते हैं:

    data Weapon = T Int | S Int 
    

    और इसकी व्याख्या बदल ऐसी है कि मूल्य T n वास्तव में T (2 * n) प्रतिनिधित्व करता है और मूल्य S nS (2 * n + 1) प्रतिनिधित्व करता है। तो कोई फर्क नहीं पड़ता कि n :: Int आप चुनते हैं, T n मान्य होगा क्योंकि आप इसे "T -of-2 * n" मान के रूप में मानेंगे।

    +0

    अच्छा। क्या आपको पता है कि मुझे इस तरह की चाल के और उदाहरण कहां मिल सकते हैं? – danidiaz

    +2

    और आप हैंडवेव को 'इंटेगर' से हटा सकते हैं। – dfeuer

    +2

    @danidiaz: Agda मानक पुस्तकालय में इस पैटर्न के कई उदाहरण हैं, उदा। http://agda.github.io/agda-stdlib/html/Data.Integer.html#915 – Cactus

    5

    यदि आप नाट्स का उपयोग करने के लिए खुद को प्रतिबंधित करने के इच्छुक हैं, और उचित उन्नत प्रकार के जादू का उपयोग करने के साथ ठीक हैं, तो आप GHC.TypeLits का उपयोग कर सकते हैं।

    {-# LANGUAGE DataKinds, GADTs, TypeOperators, KindSignatures #-} 
    
    import GHC.TypeLits 
    
    data Weapon (n :: Nat) where 
        Banana  :: n ~ (2 * m) => Weapon n 
        PointyStick :: n ~ (2 * m + 1) => Weapon n 
    
    banana :: Weapon 2 
    banana = Banana 
    
    pointyStick :: Weapon 3 
    pointyStick = PointyStick 
    

    अपने लिए प्रयास करें, कि यह गलत (विषम/यहां तक) संख्याओं के साथ संकलित नहीं होगा।

    संपादित करें: हालांकि अधिक व्यावहारिक शायद कैक्टस दृष्टिकोण है।

    +0

    यह अनुरोध के लिए दोहरी तरह लगता है। दिलचस्प है, यद्यपि। – dfeuer

    +0

    मैं मॉन्टी पायथन संदर्भ की भी सराहना करता हूं। – dfeuer

    +0

    आपको ऐसा क्यों लगता है कि यह दोहरी है? रनटाइम पर इन नटी हथियार बनाने के लिए आपको कुछ लंबाई तक जाना होगा, लेकिन यह कठिन नहीं है (और एक अलग तरह का सवाल)। यह गारंटी देता है कि हालांकि केले और अजीब पॉइंट स्टिक भी बनते हैं। – ibotty

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