2012-09-04 8 views
12

असली दुनिया हास्केल इस उदाहरण है:क्या कक्षा उदाहरण एक लूप है अगर जीएचसी चेतावनी दे सकता है?

class BasicEq3 a where 
    isEqual3 :: a -> a -> Bool 
    isEqual3 x y = not (isNotEqual3 x y) 

    isNotEqual3 :: a -> a -> Bool 
    isNotEqual3 x y = not (isEqual3 x y) 

instance BasicEq3 Bool 

और जब मैं GHCi में इसे चलाने:

#> isEqual3 False False 
out of memory 

तो, आप 2 तरीकों में से कम से कम एक को लागू करना या इसे पाश होगा। और आपको यह चुनने की लचीलापन मिलती है कि कौन सा साफ है।

मेरे पास सवाल है, क्या कोई चेतावनी या कुछ पाने का कोई तरीका है यदि पर्याप्त डिफ़ॉल्ट ओवरराइड नहीं किया गया है और डिफ़ॉल्ट एक लूप बनाते हैं? यह मेरे लिए अजीब लगता है कि संकलक जो इतना पागल स्मार्ट है इस उदाहरण के साथ ठीक है।

उत्तर

12

मुझे लगता है कि जीएचसी के लिए "अखंड" चक्रीय निर्भरता के मामले में चेतावनी जारी करना बिल्कुल ठीक है। उन पंक्तियों के साथ एक टिकट भी है: http://hackage.haskell.org/trac/ghc/ticket/6028

सिर्फ इसलिए कि कुछ "अनावश्यक" है इसका मतलब यह नहीं है कि समस्या का कोई उदाहरण प्रभावी ढंग से हल नहीं किया जा सकता है। जीएचसी (या किसी अन्य हास्केल कंपाइलर) में पहले से ही बहुत सी जानकारी की आवश्यकता है, और यदि उपयोगकर्ता चक्रीय निर्भरता को "तोड़ने" के बिना कक्षा को तुरंत चालू कर रहा है तो चेतावनी जारी करना पूरी तरह से संभव होगा। और यदि संकलक दुर्लभ मामलों में गलत पोस्ट करता है जैसा कि पिछले पदों में उदाहरण दिया गया है, तो उपयोगकर्ता को -nowarnundefinedcyclicmethods या जीएचसी को चुप रहने के लिए एक समान तंत्र हो सकता है। लगभग हर दूसरे मामले में, चेतावनी का स्वागत किया जाएगा और प्रोग्रामर उत्पादकता में जोड़ देगा; लगभग हमेशा एक मूर्ख बग क्या है से परहेज।

+0

हां! रोकथाम की समस्या के मामले में, एग्डा जैसी कुल भाषाएं साबित करती हैं कि सामान्य रूप से असफल होने वाली समस्याएं बड़े और दिलचस्प हल करने योग्य सबसेट हो सकती हैं। –

+0

"'सरल सादे अपरिहार्य' हाथ से एक कंपाइलर तकनीक को खारिज करने के लिए पर्याप्त मानदंड नहीं है। सामान्य मामलों में अनावश्यक समस्याएं जो कि अनिश्चित हैं, विशिष्ट मामलों में काफी डरावनी हैं।" http://c2.com/cgi/wiki?SfficientlySmartCompiler –

+0

ट्रैक टिकट को बकवास कारण के लिए बंद कर दिया गया है: कोई व्यक्ति '''' या' negate' के बिना 'न्यू' उदाहरण परिभाषित कर सकता है लेकिन जहां '+' मूल्यांकन नहीं करता है इसका दूसरा तर्क है, इसलिए इसकी स्पष्ट रूप से परिपत्र परिभाषा ठीक समाप्त हो जाएगी। '-' या' negate' को परिभाषित किए बिना गैर-समाप्ति से बाहर निकलने का यह एकमात्र तरीका है। मुझे लगता है कि ये विचित्र उपयोग के मामले हैं, और यदि आप '_'' का उपयोग कर परिपत्र परिभाषाओं से बचना चाहते हैं तो 'नोर्नंडिफाइनसाइक्लिकमाइंड्स' निर्दिष्ट करना ठीक है। हालांकि मुझे पता नहीं है कि ट्रैक पर इसे कैसे बढ़ाया जाए। – AndrewC

12

नहीं, मुझे डर है कि जीएचसी ऐसा कुछ नहीं करता है। यह भी सामान्य में संभव नहीं है।

आप देखते हैं, एक प्रकार के वर्ग के तरीके एक उपयोगी तरीके से पारस्परिक रूप से पुनरावर्ती हो सकते हैं। यहां इस तरह के एक प्रकार के वर्ग का एक संक्षिप्त उदाहरण है। sumOdds या sumEvens को परिभाषित करने के लिए यह बिल्कुल ठीक है, भले ही उनके डिफ़ॉल्ट कार्यान्वयन एक-दूसरे के मामले में हों।

class Weird a where 
    measure :: a -> Int 

    sumOdds :: [a] -> Int 
    sumOdds [] = 0 
    sumOdds (_:xs) = sumEvens xs 

    sumEvens :: [a] -> Int 
    sumEvens [] = 0 
    sumEvens (x:xs) = measure x + sumOdds xs 
+0

दिलचस्प, मैंने कभी इसके बारे में सोचा नहीं। –

+0

असल में, कई मानक वर्ग ऐसा करते हैं (उदाहरण के लिए, 'ऑर्ड')। यही वह जगह है जहां से "न्यूनतम पूर्ण परिभाषा" आती है। –

+2

क्या आप पारस्परिक रूप से रिकर्सिव का मतलब था? Corecursive अलग है – nponeccop

2

मुझे ऐसा नहीं लगता है। मुझे चिंता है कि आप संकलक को रोकने की समस्या को हल करने की उम्मीद कर रहे हैं! सिर्फ इसलिए कि दो कार्यों को एक दूसरे के संदर्भ में परिभाषित किया गया है, इसका मतलब यह नहीं है कि यह एक खराब डिफ़ॉल्ट वर्ग है। इसके अलावा, मैंने अतीत में कक्षाओं का उपयोग किया है जहां मुझे उपयोगी कार्यक्षमता जोड़ने के लिए instance MyClass MyType लिखने की आवश्यकता है। तो उस वर्ग के बारे में आपको चेतावनी देने के लिए संकलक से पूछना यह अन्य, वैध कोड के बारे में शिकायत करने के लिए कह रहा है।

[बेशक, विकास के दौरान ghci का उपयोग करें और इसे लिखने के बाद प्रत्येक कार्य का परीक्षण करें! उपयोग HUnit और/या QuickCheck, बस इस सामान के बारे में सुनिश्चित कोई भी अंतिम कोड में समाप्त होता है बनाने के लिए।]

6

नहीं है, वहाँ नहीं है, के बाद से यदि संकलक इस संकल्प कर सकता है, कि सुलझाने के बराबर होगा समस्या निवारण आम तौर पर, तथ्य यह है कि दो कार्य एक दूसरे को "लूप" पैटर्न में कॉल करते हैं, यह निष्कर्ष निकालने के लिए पर्याप्त नहीं है कि वास्तव में कार्यों में से किसी एक को कॉल करने के परिणामस्वरूप लूप होगा।

एक (काल्पनिक) उदाहरण का उपयोग करने के लिए,

collatzOdd :: Int -> Int 
collatzOdd 1 = 1 
collatzOdd n = let n' = 3*n+1 in if n' `mod` 2 == 0 then collatzEven n' 
            else collatzOdd n' 

collatzEven :: Int -> Int 
collatzEven n = let n' = n `div` 2 in if n' `mod` 2 == 0 then collatzEven n' 
             else collatzOdd n' 

collatz :: Int -> Int 
collatz n = if n `mod` 2 == 0 then collatzEven n else collatzOdd n 

(बेशक यह Collatz conjecture लागू करने के लिए नहीं सबसे प्राकृतिक तरीका है, लेकिन यह पारस्परिक रूप से पुनरावर्ती कार्यों को दिखाता है।)

अब collatzEven और collatzOdd एक दूसरे पर निर्भर करते हैं, लेकिन कोलात्ज़ अनुमान यह बताता है कि collatz को कॉल करने से सभी सकारात्मक n समाप्त हो जाता है। यदि जीएचसी निर्धारित कर सकता है कि collatzOdd और collatzEven की कक्षा में डिफ़ॉल्ट परिभाषाओं की पूरी परिभाषा थी या नहीं, तो जीएचसी को कोलात्ज़ अनुमान को हल करने में सक्षम होगा! (यह निश्चित रूप से हल करने की समस्या की अनावश्यकता का सबूत नहीं है, लेकिन यह स्पष्ट करना चाहिए कि यह निर्धारित करना क्यों कि एक पारस्परिक रूप से कार्य करने का सेट अच्छी तरह से परिभाषित किया गया है, जैसा कि यह प्रतीत होता है उतना छोटा नहीं है।)

इन सामान्य, चूंकि जीएचसी स्वचालित रूप से यह निर्धारित नहीं कर सकता है, इसलिए हास्केल कक्षाओं के लिए प्रलेखन कक्षा के उदाहरण बनाने के लिए आवश्यक "न्यूनतम पूर्ण परिभाषा" प्रदान करेगा।

+8

यह अच्छा होगा अगर न्यूनतम पूर्ण परिभाषा को इस तरह से निर्दिष्ट करने का कोई तरीका था कि संकलक इसे देख सके, हालांकि। – hammar

+0

यह एक अच्छा मुद्दा है - शायद कक्षा परिभाषा के साथ एक कंपाइलर प्रगामा। – AndrewC

2

मेरी व्यक्तिगत राय में, डिफ़ॉल्ट तंत्र अनावश्यक और मूर्ख है।

notEq3FromEq3 :: (a -> a -> Bool) -> (a -> a -> Bool) 
notEq3FromEq3 eq3 = (\x y -> not (eq3 x y)) 

eq3FromNotEq3 :: (a -> a -> Bool) -> (a -> a -> Bool) 
eq3FromNotEq3 ne3 = (\x y -> not (ne3 x y)) 

(वास्तव में, इन दोनों परिभाषाओं बराबर हैं, लेकिन यह है कि सामान्य रूप में सही नहीं होगा): यह आसान के लिए वर्ग लेखक सिर्फ साधारण कार्यों के रूप में चूक प्रदान करने के लिए किया जाएगा। फिर एक उदाहरण इस प्रकार दिखता है:

instance BasicEq3 Bool where 
    isEqual3 True True = True 
    isEqual3 False False = True 
    isEqual3 _ _ = False 

    isNotEqual3 = notEq3FromEq3 isEqual3 

और कोई डिफ़ॉल्ट आवश्यकता नहीं है। फिर यदि आप परिभाषा प्रदान नहीं करते हैं तो जीएचसी आपको चेतावनी दे सकता है, और किसी भी अप्रिय लूप को आपके कोड में स्पष्ट रूप से लिखा जाना चाहिए।

यह मौजूदा उदाहरणों को प्रभावित किए बिना डिफ़ॉल्ट परिभाषाओं के साथ कक्षा में नई विधियों को जोड़ने की सुगम क्षमता को हटा देता है, लेकिन यह मेरे विचार में इतना बड़ा लाभ नहीं है। उपरोक्त दृष्टिकोण सिद्धांत रूप में अधिक लचीला है: आप उदा। किसी भी तुलना ऑपरेटर को लागू करने के लिए Ord इंस्टेंस को अनुमति देने वाले फ़ंक्शंस प्रदान करें।

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