हास्केल: सूचियाँ, सारणियाँ, क्षेत्र, अनुक्रम


230

मैं हास्केल सीख रहा हूं और हास्केल सूचियों के प्रदर्शन मतभेद और (अपनी भाषा डालें) के सरणियों के बारे में कुछ लेख पढ़ रहा हूं।

एक शिक्षार्थी होने के नाते मैं स्पष्ट रूप से केवल प्रदर्शन के अंतर के बारे में सोचने के बिना सूचियों का उपयोग करता हूं। मैंने हाल ही में जांच शुरू की और हास्केल में उपलब्ध कई डेटा संरचना पुस्तकालयों को पाया।

क्या कोई कृपया डेटा संरचनाओं के कंप्यूटर विज्ञान के सिद्धांत में बहुत गहराई तक जाने के बिना सूचियों, सरणियों, क्षेत्रों, अनुक्रमों के बीच का अंतर बता सकता है?

इसके अलावा, क्या कुछ सामान्य पैटर्न हैं जहां आप एक के बजाय एक डेटा संरचना का उपयोग करेंगे?

क्या डेटा संरचनाओं के कोई अन्य रूप हैं जो मुझे याद आ रहे हैं और उपयोगी हो सकते हैं?


1
सूची बनाम सरणियों के बारे में इस उत्तर पर एक नज़र डालें: stackoverflow.com/questions/8196667/haskell-arrays-vs-lists वैक्टर में अधिकतर सरणियों के समान प्रदर्शन होता है, लेकिन एक बड़ा एपीआई।
ग्रेज़गोरज़ चरूपा

डेटा को देखना अच्छा लगेगा। यहां भी चर्चा की गई। यह विशेष रूप से बहुआयामी डेटा के लिए एक उपयोगी डेटा संरचना की तरह लगता है।
मार्टिन कैपोडिसी

जवाबों:


339

रॉक सूचीबद्ध करता है

अब तक हास्केल में अनुक्रमिक डेटा के लिए सबसे अनुकूल डेटा संरचना सूची है

 data [a] = a:[a] | []

सूचियाँ आपको s (1) कॉन्स और पैटर्न का मेल देती हैं। मानक पुस्तकालय, और कहा कि प्रस्तावना बात के लिए, उपयोगी सूची कार्यों से भरा हुआ है कि ऐसा करना चाहिए अपने कोड कूड़े ( foldr, map, filter)। सूचियाँ लगातार , उर्फ ​​विशुद्ध रूप से कार्यात्मक हैं, जो बहुत अच्छा है। हास्केल सूचियाँ वास्तव में "सूचियाँ" नहीं हैं क्योंकि वे सहवर्ती हैं (अन्य भाषाएं इन धाराओं को कहते हैं) इसलिए चीजें पसंद हैं

ones :: [Integer]
ones = 1:ones

twos = map (+1) ones

tenTwos = take 10 twos

अद्भुत ढंग से काम करें। अनंत डेटा संरचनाएं रॉक।

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

दूसरी ओर

सूचियों के साथ पहली समस्या यह है कि उनमें अनुक्रमण करने के लिए (!!)k (k) समय लगता है, जो कष्टप्रद है। इसके अलावा, ऐप्पल धीमा हो सकता है ++, लेकिन हास्केल के आलसी मूल्यांकन मॉडल का मतलब है कि इन्हें पूरी तरह से परिशोधन के रूप में माना जा सकता है, यदि वे बिल्कुल भी होते हैं।

सूचियों के साथ दूसरी समस्या यह है कि उनके पास खराब डेटा इलाका है। जब वास्तविक मेमोरी में ऑब्जेक्ट्स एक-दूसरे के बगल में नहीं रखे जाते हैं तो वास्तविक प्रोसेसर उच्च स्थिरांक पैदा करते हैं। इसलिए, C ++ std::vectorमें किसी भी शुद्ध लिंक्ड लिस्ट डेटा स्ट्रक्चर की तुलना में "स्नोक" (अंत में ऑब्जेक्ट्स डालना) है जो मुझे पता है, हालांकि यह हस्केल की सूचियों की तुलना में इतना कम अनुकूल नहीं है।

सूचियों के साथ तीसरी समस्या यह है कि उनके पास अंतरिक्ष की खराब क्षमता है। अतिरिक्त पॉइंटर्स के बंच आपके स्टोरेज (एक स्थिर कारक द्वारा) को धक्का देते हैं।

अनुक्रम कार्यात्मक हैं

Data.Sequenceआंतरिक रूप से उंगली के पेड़ों पर आधारित है (मुझे पता है, आप यह जानना नहीं चाहते हैं) जिसका अर्थ है कि उनके पास कुछ अच्छे गुण हैं

  1. विशुद्ध रूप से कार्यात्मक। Data.Sequenceएक पूरी तरह से निरंतर डेटा संरचना है।
  2. पेड़ की शुरुआत और अंत में तेजी से प्रवेश। ϴ (1) (परिशोधित) पहला या अंतिम तत्व प्राप्त करने के लिए, या पेड़ों को जोड़ने के लिए। सूची में सबसे तेजी से कर रहे हैं, Data.Sequenceसबसे अधिक धीमी गति से है।
  3. ϴ (लॉग एन) अनुक्रम के मध्य तक पहुंच। इसमें नए क्रम बनाने के लिए मान सम्मिलित करना शामिल है
  4. उच्च गुणवत्ता एपीआई

दूसरी ओर, Data.Sequenceडेटा स्थानीयता समस्या के लिए बहुत कुछ नहीं करता है, और केवल परिमित संग्रह के लिए काम करता है (यह सूचियों से कम आलसी है)

दिल के बेहोश होने के लिए ऐरे नहीं हैं

Arrays सीएस में सबसे महत्वपूर्ण डेटा संरचनाओं में से एक है, लेकिन वे आलसी शुद्ध कार्यात्मक दुनिया के साथ बहुत अच्छी तरह से फिट नहीं हैं। Arrays संग्रह के मध्य में r (1) पहुंच प्रदान करता है और असाधारण रूप से अच्छा डेटा स्थानीयता / निरंतर कारक। लेकिन, चूंकि वे हास्केल में बहुत अच्छी तरह से फिट नहीं हैं, इसलिए वे उपयोग करने के लिए एक दर्द है। वर्तमान मानक पुस्तकालय में वास्तव में विभिन्न प्रकार के विभिन्न प्रकार हैं। इनमें पूरी तरह से निरंतर सरणियाँ, IO मोनाड के लिए परिवर्तनशील सरणियाँ, ST मोनाड के लिए उत्परिवर्ती सरणियाँ और उपरोक्त के अन-बॉक्सिंग संस्करण शामिल हैं। अधिक चेक आउट के लिए हैस्केल विकी

वेक्टर एक "बेहतर" ऐरे है

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

आपके पास अन्य विकल्प हैं

यदि आप सूची को अंत में कुशलता से सम्मिलित करने की क्षमता चाहते हैं, तो आप एक अंतर सूची का उपयोग कर सकते हैं । प्रदर्शन को [Char]खराब करने वाली सूचियों का सबसे अच्छा उदाहरण इससे आता है, जिसमें से प्रस्तावना के रूप में उतारा गया है StringCharसूचियाँ दृढ़ हैं, लेकिन सी स्ट्रिंग्स की तुलना में 20 गुना धीमी गति से चलने की प्रवृत्ति है, इसलिए बेझिझक उपयोग करें Data.Textया बहुत तेज़ करें Data.ByteString। मुझे यकीन है कि अन्य अनुक्रम उन्मुख पुस्तकालय हैं जो मैं अभी नहीं सोच रहा हूं।

निष्कर्ष

90 +% उस समय जब मुझे हास्केल सूचियों में अनुक्रमिक संग्रह की आवश्यकता होती है, वे सही डेटा संरचना हैं। सूची पुनरावृत्तियों की तरह हैं, जो कार्य सूचियों का उपभोग करते हैं वे आसानी से इनमें से किसी भी अन्य डेटा संरचनाओं के साथ उपयोग किए जा सकते हैं, जिनके साथ toListवे आते हैं। एक बेहतर दुनिया में प्रस्तावना पूरी तरह से पैरामीट्रिक होगी कि वह किस प्रकार के कंटेनर का उपयोग करता है, लेकिन वर्तमान []में मानक पुस्तकालय का उपयोग करता है । तो, सूचियों का उपयोग करना (लगभग) हर जहाँ निश्चित रूप से ठीक है।
आप सूची के अधिकांश कार्यों के पूरी तरह से पैरामीट्रिक संस्करण प्राप्त कर सकते हैं (और उनका उपयोग करने के लिए महान हैं)

Prelude.map                --->  Prelude.fmap (works for every Functor)
Prelude.foldr/foldl/etc    --->  Data.Foldable.foldr/foldl/etc
Prelude.sequence           --->  Data.Traversable.sequence
etc

वास्तव में, Data.Traversableएक एपीआई को परिभाषित करता है जो किसी भी चीज की तरह कम या ज्यादा सार्वभौमिक है "जैसे सूची"।

फिर भी, हालांकि आप अच्छे हो सकते हैं और केवल पूरी तरह से पैरामीट्रिक कोड लिख सकते हैं, हम में से अधिकांश नहीं हैं और सभी जगह सूची का उपयोग करते हैं। यदि आप सीख रहे हैं, तो मेरा सुझाव है कि आप भी ऐसा करें।


संपादित करें: टिप्पणियों के आधार पर मुझे लगता है कि मैंने कभी नहीं समझाया कि कब Data.Vectorबनाम का उपयोग किया जाए Data.Sequence। ऐरे और वैक्टर बेहद तेजी से अनुक्रमण और टुकड़ा करने की क्रिया प्रदान करते हैं, लेकिन मौलिक रूप से क्षणिक (अनिवार्य) डेटा संरचनाएं हैं। शुद्ध कार्यात्मक डेटा संरचनाएँ जैसे Data.Sequenceऔर पुराने मानों से []कुशलतापूर्वक नए मान उत्पन्न करती हैं जैसे कि आपने पुराने मूल्यों को संशोधित किया था।

  newList oldList = 7 : drop 5 oldList

पुरानी सूची को संशोधित नहीं करता है, और इसे कॉपी करने की आवश्यकता नहीं है। तो भले ही oldListअविश्वसनीय रूप से लंबा हो, यह "संशोधन" बहुत तेज़ होगा। उसी प्रकार

  newSequence newValue oldSequence = Sequence.update 3000 newValue oldSequence 

newValueअपने 3000 तत्व के स्थान पर एक नए अनुक्रम का निर्माण करेगा । फिर से, यह पुराने अनुक्रम को नष्ट नहीं करता है, यह सिर्फ एक नया बनाता है। लेकिन, यह बहुत कुशलता से करता है, ओ (लॉग (मिनट (के, घुटने)) जहां n अनुक्रम की लंबाई है, और कश्मीर आप को संशोधित करते हैं।

आप आसानी से Vectorsऔर के साथ ऐसा नहीं कर सकते Arrays। उन्हें संशोधित किया जा सकता है लेकिन यह वास्तविक अनिवार्य संशोधन है, और इसलिए नियमित हास्केल कोड में ऐसा नहीं किया जा सकता है। इसका मतलब है Vectorकि पैकेज में संचालन जो संशोधन करना चाहते हैं snocऔर consपूरे वेक्टर को कॉपी करना है ताकि O(n)समय लग सके। इसका एकमात्र अपवाद यह है कि आप मोनड (या ) के Vector.Mutableअंदर उत्परिवर्तित संस्करण ( ) का उपयोग कर सकते हैं और अपने सभी संशोधनों को उसी तरह कर सकते हैं जैसे आप एक अनिवार्य भाषा में करेंगे। जब आप कर लेते हैं, तो आप अपने वेक्टर को उन अपरिवर्तनीय संरचना में "फ्रीज" करते हैं जिन्हें आप शुद्ध कोड के साथ उपयोग करना चाहते हैं। STIO

मेरी भावना यह है कि Data.Sequenceयदि कोई सूची उचित नहीं है, तो आपको उपयोग करने के लिए डिफ़ॉल्ट होना चाहिए । Data.Vectorकेवल तभी उपयोग करें जब आपके उपयोग पैटर्न में कई संशोधन करना शामिल नहीं है, या यदि आपको एसटी / आईओ मोनैड के भीतर अत्यधिक उच्च प्रदर्शन की आवश्यकता है।

अगर यह सब बात STमोनाद आपको उलझन में छोड़ रही है: सभी और अधिक कारण शुद्ध तेज और सुंदर से चिपके रहते हैं Data.Sequence


45
एक अंतर्दृष्टि मैंने सुना है कि सूचियाँ मूल रूप से हास्केल में डेटा संरचना के रूप में एक नियंत्रण संरचना हैं। और यह समझ में आता है: जहां आप एक अलग भाषा में लूप के लिए सी-शैली का उपयोग करेंगे, आप [1..]हास्केल में एक सूची का उपयोग करेंगे । सूची का उपयोग बैकग्राउंडिंग जैसी मज़ेदार चीज़ों के लिए भी किया जा सकता है। नियंत्रण संरचनाओं (प्रकार) के रूप में उनके बारे में सोचने से वास्तव में यह समझने में मदद मिली कि उनका उपयोग कैसे किया जाता है।
तिखन जेल्विस

21
बहुत बढ़िया जवाब। मेरी एकमात्र शिकायत यह है कि "अनुक्रम कार्यात्मक हैं" उन्हें थोड़ा सा रेखांकित कर रहा है। अनुक्रम कार्यात्मक awesomesauce हैं। उनके लिए एक और बोनस तेजी से जुड़ना और विभाजित होना (लॉग एन) है।
दान बर्टन

3
@ डनबटन मेला। मैंने शायद अंडरस्सेल किया था Data.Sequence। कंप्यूटिंग के इतिहास में उंगली के पेड़ सबसे भयानक आविष्कारों में से एक हैं (गुइबास को शायद किसी दिन ट्यूरिंग पुरस्कार मिलना चाहिए) और Data.Sequenceएक उत्कृष्ट कार्यान्वयन है और इसमें एक बहुत ही उपयोगी एपीआई है।
फिलिप जेएफ

3
"UseData.Vector केवल अगर आपके उपयोग के पैटर्न में कई संशोधन करना शामिल नहीं है, या यदि आपको ST / IO मोनाड्स के भीतर अत्यधिक उच्च प्रदर्शन की आवश्यकता है .." दिलचस्प शब्दांकन, क्योंकि यदि आप कई संशोधन कर रहे हैं (जैसे बार-बार (100k बार) 100k तत्वों विकसित हो), तो आप कर जरूरत अनुसूचित जनजाति / आईओ वेक्टर स्वीकार्य प्रदर्शन प्राप्त करने,
misterbee

4
शुद्ध (वैक्टर) वैक्टर और नकल के बारे में चिंताएँ धारा के संलयन द्वारा आंशिक रूप से कम हो जाती हैं, उदाहरण के import qualified Data.Vector.Unboxed as VU; main = print (VU.cons 'a' (VU.replicate 100 'b'))लिए: कोर में 404 बाइट्स (101 वर्ण) के एक ही आवंटन के लिए संकलित करता है: hpaste.org/65015
फन्टरसाल्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.