2011-12-21 19 views
16

Haskell 2010 report के अनुसार, init निम्नलिखित के रूप में परिभाषित किया गया है:आंशिक कार्य क्यों है?

init    :: [a] -> [a] 
init [x]   = [] 
init (x:xs)  = x : init xs 
init []   = error "Prelude.init: empty list" 

base-4.4.1.0 यह इसी तरह परिभाषित करता है। मेरे लिए, ऐसा लगता है कि यह पूरी तरह से स्वीकार्य और सहज ज्ञान युक्त है करने के लिए होगा:

init [] = [] 

जो init कुल समारोह होगा। चूंकि इस परिभाषा ने इसे हैकेल 2010 की रिपोर्ट में बनाया है, मुझे लगता है कि इसके लिए तर्क हैं। क्या यह मामला है या परंपरा और पीछे की संगतता के कारण इस तरह से परिभाषित किया गया है?

+11

init को सूची के अंतिम तत्व के अलावा सभी को वापस करना चाहिए। रिक्त सूचियों में अंतिम तत्व नहीं है, इसलिए कोई भी इनिट नहीं है। इसके अलावा, इनवेरिएंट 'xs == init xs ++ [last xs]' अब आपके पास वर्णित तरीके से परिभाषित नहीं किया जाएगा। –

+0

@ निकलास, लेकिन इसी तरह, आप कह सकते हैं, init उन सभी तत्वों को वापस करना चाहिए जो अंतिम तत्व नहीं हैं, ऐसे कोई तत्व नहीं हैं इसलिए हमें खाली सूची वापस करनी चाहिए। – HaskellElephant

+0

@ निकलास, क्या यह अभी भी नहीं होगा? मेरा मतलब है कि अगर आखिरी [] = [] –

उत्तर

19

इसी कारण tail [][] नहीं है; यह अपरिवर्तनशीलताओं कि इन कार्यों को परिभाषित टूट जाता है। हम कह रही है कि अगर head और tail एक मूल्य xs, तो xs ≡ head xs : tail xs के लिए परिभाषित कर रहे हैं द्वारा head और tail परिभाषित कर सकते हैं।

निकलास ने बताया, हम जिस आविष्कार को init और last परिभाषित करना चाहते हैं xs ≡ init xs ++ [last xs] है। यह सच है कि last या तो खाली सूची में परिभाषित नहीं है, लेकिन क्यों करता है, तो init नहीं किया जा सकता परिभाषित किया जा last चाहिए? वैसे ही जैसे यह गलत हो सकता है अगर head या tail में से एक एक इनपुट पर परिभाषित किया गया था, जबकि अन्य नहीं था, init और last दो मूल्यों है कि एक साथ मूल के बराबर हैं में एक सूची बंटवारे एक ही सिक्के के दो पहलू हैं।

एक अधिक "व्यावहारिक" दृश्य के लिए

(हालांकि आपरेशन वे का उपयोग किया है के बारे में उपयोगी अपरिवर्तनशीलताओं के मामले में कार्यक्रमों के बारे में तर्क करने में सक्षम होने के बहुत प्रायोगिक लाभ!), एक एल्गोरिथ्म init का उपयोग करता है शायद सही ढंग से एक पर व्यवहार नहीं होगा खाली सूची, और यदि init इस तरह से काम करता है, तो यह उत्पादित त्रुटियों को छिपाने के लिए काम करेगा। यह अपने आदानों के बारे में सतर्क रहना init के लिए बेहतर है ताकि विचार खाली सूची जब यह प्रयोग किया जाता है, खासकर जब यह बिल्कुल स्पष्ट है कि प्रस्तावित मूल्य यह उन मामलों में वापस जाने के लिए के लिए है नहीं है की तरह किनारे मामलों के लिए दी जाने वाली है उचित।

यहाँ एक previous Stack Overflow question के बारे में head और tail, सहित क्यों tail [] सिर्फ [] वापस नहीं करता है; जवाबों को यह बताने में मदद करनी चाहिए कि इन आविष्कार इतने महत्वपूर्ण क्यों हैं।

+0

मुझे लगता है कि आपका मतलब था "आखिरी' नहीं होने पर 'init' को क्यों परिभाषित किया जाना चाहिए?"। – HaskellElephant

5

यह init [] = [] के लिए भयानक हो सकता है, विशेष रूप से यह xs == init xs ++ [last xs] और length xs == 1 + length (init xs) जैसे महत्वपूर्ण अपरिवर्तनशीलताओं टूट जाएगा, और इसलिए त्रुटियों को छिपाने।

11

क्योंकि मैं सवाल दिलचस्प है, मैं इस विषय पर अपने विचारों को नीचे लिखने का फैसला किया:

तार्किक

हम "के रूप में सूची init xs परिभाषित करते हैं, कि, यदि आप पर last xs डाल कहो अपने अंत, XS के बराबर है यह बराबर है करने के लिए:।। init xs अपने पिछले तत्व के बिना सूची है xs == [] के लिए, कोई अंतिम तत्व मौजूद है, इसलिए init [] अपरिभाषित हो गया है।

आप इस के लिए एक विशेष मामले के जोड़ सकता है, में की तरह "अगर XS खाली सूची है, तो init xs खाली सूची, अन्यथा init xs सूची है, वह यह है कि अगर आप अपने अंत पर last xs शब्दों में कहें, बराबर है xs "। ध्यान दें कि यह कितना अधिक verbose और कम साफ है। हम अतिरिक्त जटिलता पेश करते हैं, लेकिन इसके लिए क्या?

सहज

init [1,2,3,4] == [1,2,3] 
init [1,2,3] == [1,2] 
init [1,2]  == [1] 
init [1]  == [] 
init []  == ?? 

नोट कैसे समीकरणों के दाएँ हाथ की ओर पर सूचियों की लंबाई बाएं हाथ की ओर की लंबाई के साथ कम हो जाती है। मेरे लिए, इस श्रृंखला को एक समझदार तरीके से जारी नहीं रखा जा सकता है, क्योंकि दाएं तरफ की सूची का नकारात्मक आकार होना चाहिए!

प्रैक्टिकल

के रूप में अन्य लोगों ने बताया है, एक विशेष मामला init या एक तर्क के रूप खाली सूची के लिए tail के लिए से निपटने को परिभाषित करने, स्थितियों में मुश्किल से जगह त्रुटियों लागू कर सकते हैं कार्यों कोई समझदार हो सकता है जहां खाली सूची के परिणामस्वरूप, लेकिन अभी भी इसके लिए अपवाद नहीं बनाते हैं!

इसके अलावा, मैं सोच सकता हूं कि init [][] का मूल्यांकन करने के लिए वास्तव में इसका कोई फायदा नहीं होगा, तो उस अतिरिक्त जटिलता का परिचय क्यों दें? प्रोग्रामिंग सादगी के बारे में है और विशेष रूप से हास्केल शुद्धता के बारे में है, क्या आप सहमत नहीं होंगे?

+3

आपकी अंतर्ज्ञानी श्रृंखला की एक स्पष्ट और समझदार निरंतरता है, और यह पहचानना है कि एक स्पष्ट रूप से अपरिभाषित मामले को इस तरह प्रदर्शित करने की आवश्यकता है। 'Init' के लिए सही प्रकार इस प्रकार है [ए] -> शायद [ए]', 'init [] = कुछ भी नहीं '। –

+0

हाँ, लेकिन इसका मतलब यह होगा कि जब भी हम 'init' कहते हैं तो हमें विशेष मामले को संभालने की आवश्यकता होती है (उदाहरण के लिए प्रत्येक कॉल को 'से just' में लपेटकर) ... मुझे यह बहुत पसंद नहीं है। –

+3

क्या? नहीं, 'से जस्ट' एक बिल्कुल भयानक काम है और बिल्कुल अस्तित्व में नहीं होना चाहिए। करने के लिए सही काम हर मामले को संभालना है और आंशिक कार्यों को पहले स्थान पर नहीं लिखना है। –

8

वास्तविक मुद्दा यह है कि init, जैसा कि परिभाषित किया गया है, संभवतः काम नहीं कर सकता; यह एक अंतर्निहित आंशिक कार्य है, जैसे head और tail। मैं इस बात से रोमांचित नहीं हूं कि मानक पुस्तकालयों में कितने जानबूझकर आंशिक कार्य मौजूद हैं, लेकिन यह एक और मामला है।

  • व्याख्या यह है कि यह दी गई सूची पर एक फिल्टर है ले लो, सभी तत्वों को जो पिछले तत्व नहीं हैं अपरिवर्तनशीलताओं रखते हुए, और छोड़:

    init के रूप में दिया बचाया नहीं जा सकता है के बाद से, हम दो विकल्प हैं length और last के संबंध में। इसे reverse . drop 1 . reverse के रूप में लिखा जा सकता है, जो एक स्पष्ट सामान्यीकरण का सुझाव देता है। वांछित अगर हम take के लिए एक समान समकक्ष पेश कर सकते हैं।

  • पहचानें कि init आंशिक फ़ंक्शन को परिभाषित करता है, और इसे सही प्रकार, [a] -> Maybe [a] देता है। त्रुटि को Nothing के साथ सही ढंग से बदला जा सकता है।

+0

'inits' जैसे एक स्पष्ट सामान्यीकरण :) – ehird

+0

@ehird, अच्छी तरह से 'inits'' init [] = []] 'inits [] == [[]]' के साथ अधिक है। एक तरह से यह कह रहा है कि [] का एकमात्र init [] है, यदि यह 'init [] = अपरिभाषित' के साथ चिपकना है, तो यह 'inits [] == []' होना चाहिए। – HaskellElephant

+0

हम्म, मैं सहमत नहीं हूं। 'लंबाई (inits xs) ≡ लंबाई xs + 1', तो 'लंबाई (inits []) ≡ 1', और संभवतः एकमात्र तत्व यह संभवतः' [] 'है। – ehird

1

यह पारंपरिक है। उस समय, उन्होंने मनमाने ढंग से पसंद किया, और अब लोगों का उपयोग किया जाता है।

उन्होंने initlast के समान बनाया, ताकि वे दोनों खाली सूचियों पर विफल हो जाएं। (tail और head इस तरह की एक और जोड़ी हैं।)

लेकिन जैसा कि आप सुझाव देते हैं, वे विपरीत चुन सकते थे। प्रीलूड में आसानी से tail' = drop 1 फ़ंक्शन शामिल हो सकता है, साथ ही init' फ़ंक्शन से मेल खाता है, जिससे खाली सूचियां मिलती हैं।मुझे नहीं लगता कि यह कोई समस्या होगी-मैं इनवेरिएंट और मुफ्त प्रमेय की सभी बातों से असहज हूं। इसका मतलब यह होगा कि init' अपने साथी last से थोड़ा अलग था; बस इतना ही।

(last और head एक और कहानी हैं: उनके हस्ताक्षर [a] -> a है, तो वे वापस एक तत्व देना चाहिए, और वे हवा में से एक नहीं कर सकते हैं इसके विपरीत, init और tail के प्रकार के पाठ्यक्रम [a] -> [a] की है। , जिसका अर्थ है कि वे खाली सूचियां प्राप्त कर सकते हैं और कर सकते हैं।)

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