2010-04-13 28 views
13

के कई निर्माताओं के साथ एक डेटा प्रकार पर विचार करें:"पैटर्न मिलान"

data T = Alpha Int | Beta Int | Gamma Int Int | Delta Int 

मैं अगर दो मानों एक ही निर्माता के साथ उत्पादित कर रहे हैं की जाँच करने के लिए एक समारोह लिखना चाहते हैं:

sameK (Alpha _) (Alpha _) = True 
sameK (Beta _) (Beta _) = True 
sameK (Gamma _ _) (Gamma _ _) = True 
sameK _ _ = False 

sameK बनाए रखना बहुत मजेदार नहीं है, इसे आसानी से शुद्धता के लिए जांच नहीं किया जा सकता है। उदाहरण के लिए, जब नए निर्माता T में जोड़े जाते हैं, तो sameK को अपडेट करना भूलना आसान है। मैं एक उदाहरण देना करने के लिए एक पंक्ति को छोड़ दिया:

-- it’s easy to forget: 
-- sameK (Delta _) (Delta _) = True 

सवाल sameK में बॉयलरप्लेट से बचने के लिए कैसे है? या यह सुनिश्चित करने के लिए कि यह सभी T रचनाकारों के लिए जांच करता है?


वैकल्पिक हल मैंने पाया कंस्ट्रक्टर्स से प्रत्येक के लिए अलग डेटा प्रकार का उपयोग करने, Data.Typeable पाने, और एक आम प्रकार वर्ग की घोषणा है, लेकिन मैं इस समाधान पसंद नहीं है, क्योंकि यह बहुत कम पठनीय और है अन्यथा सिर्फ एक सरल बीजीय प्रकार मेरे लिए काम करता है:

{-# LANGUAGE DeriveDataTypeable #-} 

import Data.Typeable 

class Tlike t where 
    value :: t -> t 
    value = id 

data Alpha = Alpha Int deriving Typeable 
data Beta = Beta Int deriving Typeable 
data Gamma = Gamma Int Int deriving Typeable 
data Delta = Delta Int deriving Typeable 

instance Tlike Alpha 
instance Tlike Beta 
instance Tlike Gamma 
instance Tlike Delta 

sameK :: (Tlike t, Typeable t, Tlike t', Typeable t') => t -> t' -> Bool 
sameK a b = typeOf a == typeOf b 
+0

उत्तर देने के लिए सभी को धन्यवाद। आपके सभी उत्तर उपयोगी थे। – sastanin

उत्तर

10

डेटा.डेटा मॉड्यूल को देखें, विशेष रूप से toConstr फ़ंक्शन। {-# LANGUAGE DeriveDataTypeable #-} के साथ जो आपको 1-लाइन समाधान मिलेगा जो किसी भी प्रकार के लिए काम करता है जो Data.Data का उदाहरण है। आपको सभी SYB को समझने की आवश्यकता नहीं है!

अगर, किसी कारण से (हग्स के साथ फंस गया?), यह एक विकल्प नहीं है, तो यहां एक बहुत बदसूरत और बहुत धीमी हैक है। यह केवल तभी काम करता है जब आपका डेटाटाइप Show सक्षम है (उदाहरण के लिए deriving (Show) का उपयोग करके - जिसका मतलब है कि उदाहरण के लिए कोई फ़ंक्शन प्रकार नहीं है)।

constrT :: T -> String 
constrT = head . words . show 
sameK x y = constrT x == constrT y 

constrT यह दिखा रहा है, इसे काटना शब्दों में और फिर पहले हो रही द्वारा एक T मूल्य की सबसे बाहरी निर्माता की स्ट्रिंग प्रतिनिधित्व हो जाता है। मैं एक स्पष्ट प्रकार का हस्ताक्षर देता हूं ताकि आप इसे अन्य प्रकारों (और मोनोमोर्फिज्म प्रतिबंध से बचने के लिए) का उपयोग करने के लिए प्रेरित न हों।

कुछ उल्लेखनीय नुकसान:

  • यह बुरी तरह टूट जाता है जब अपने प्रकार है (जैसे data T2 = Eta Int | T2 :^: T2 के रूप में) इन्फ़िक्स कंस्ट्रक्टर्स
  • अपने कंस्ट्रक्टर्स के कुछ है एक साझा उपसर्ग, इस धीमी होने वाली है, तो है, तारों के एक बड़े हिस्से की तुलना की जानी चाहिए।
  • कस्टम show, जैसे कई लाइब्रेरी प्रकारों के साथ प्रकारों पर काम नहीं करता है।

कहा यही कारण है, यह हास्केल 98 है ... लेकिन यह है कि केवल अच्छी बात मैं इसके बारे में कह सकते हैं के बारे में है!

+1

+1' toConstr' –

+1

वाह के लिए +1! यह सिर्फ जादू है। आपका बहुत बहुत धन्यवाद! – sastanin

2

कुछ मामलों में, "स्क्रैप आपका बॉयलरप्लेट" पुस्तकालय में मदद मिलेगी।

http://www.haskell.org/haskellwiki/Scrap_your_boilerplate

+0

धन्यवाद, मैं इस दृष्टिकोण को देखूंगा। – sastanin

+0

मुझे लगता है कि मैं इन कागजात से शुरू कर सकता हूं: http://research.microsoft.com/en-us/um/people/simonpj/papers/hmap/ – sastanin

14

एक अन्य संभावित रास्ता:

sameK x y = f x == f y 
    where f (Alpha _) = 0 
     f (Beta _) = 1 
     f (Gamma _ _) = 2 
     -- runtime error when Delta value encountered 

एक रनटाइम त्रुटि आदर्श नहीं है, लेकिन चुपचाप गलत जवाब देने की तुलना में बेहतर।

+3

मुझे यह पसंद है। '-Wall' के साथ जीएचसी कृपया' एफ 'की परिभाषा में गैर-संपूर्ण पैटर्न मैचों की रिपोर्ट करता है, इसलिए रनटाइम त्रुटि को रोका जा सकता है। विचार के लिए आपका धन्यवाद। – sastanin

10

आपको जेनरिक्स लाइब्रेरी का उपयोग करना होगा जैसे स्क्रैप योर बॉयलरप्लेट या सामान्य रूप से ऐसा करने के लिए uniplate।

तुम इतनी भारी हाथ, आप खाली रिकॉर्ड शॉर्टकट के साथ एक साथ, डेव हिंटन के समाधान का उपयोग कर सकते होने के लिए नहीं करना चाहते हैं:

... 
where f (Alpha {}) = 0 
     f (Beta {}) = 1 
     f (Gamma {}) = 2 

तो तुम नहीं है कि कितने आर्ग प्रत्येक पता करने के लिए निर्माता है। लेकिन यह स्पष्ट रूप से वांछित होने के लिए कुछ छोड़ देता है।

+2

'{}' के साथ अच्छी चाल। मुझे इसके बारे में पता नहीं था। धन्यवाद। – sastanin

+2

रिकॉर्ड ब्रेसिज़ किसी और चीज से अधिक मजबूत होते हैं, इसलिए तकनीकी रूप से आपको कोष्ठक की आवश्यकता नहीं होती है। 'एफ अल्फा {} = 0' ठीक काम करेगा, हालांकि मुझे यकीन नहीं है कि यह कितना पठनीय है, यह 'एफ' जैसा दिखता है, दो तर्क लेता है। मैं कभी-कभी 'एफ अल्फा {} = 0' के साथ डबिल करता हूं ... –

1

आप निश्चित रूप से बॉयलरप्लेट को खत्म करने के लिए जेनरिक का उपयोग कर सकते हैं। आपका कोड एक पाठ्यपुस्तक उदाहरण है क्यों I (और कई अन्य कभी शीर्ष स्तर पर _ वाइल्डकार्ड का उपयोग करें)। हालांकि सभी मामलों को लिखना कठिन है, लेकिन यह बग से निपटने से कम कठिन है।

इस खुशहाल उदाहरण में मैं केवल डेव हिनटन के समाधान का उपयोग नहीं करता बल्कि सहायक फ़ंक्शन f पर एक इनलाइन प्रागमा थप्पड़ मारूंगा।

+0

हां, यही कारण है कि मैं पूछ रहा हूं। आपकी सलाह के लिए धन्यवाद। – sastanin

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