2010-02-25 13 views
8

में एक पेड़ को ट्रैवर्सिंग और फ़िल्टर करना मैं हास्केल के लिए काफी नया हूं (अभी भी पूरी तरह से मोनैड को समझने पर काम कर रहा हूं)। मैं एक समस्या है जहाँ मैं संरचना की तरह एक पेड़हैकेल

type Tree = [DataA] 

data DataA = DataA1 [DataB] 
      | DataA2 String 
      | DataA3 String [DataA] 
       deriving Show 

data DataB = DataB1 [DataA] 
      | DataB2 String 
      | DataB3 String [DataB] 
       deriving Show 

मुझे क्या करना चाहते हैं एक फिल्टर के साथ इस पार और एक नया पेड़ उत्पन्न करने में सक्षम होना है की है। उदाहरण के लिए मैं पेड़ में सभी डेटाबी 2 को "foo" में बदलना चाहता हूं।

मैंने पेड़ के उदाहरणों को देखा है जब वे एक ही डेटा अनुभाग में हैं, और रचनाकार समान हैं।

पायथन दुनिया में, मैं सिर्फ सूची को पार करता हूं, जो भी मुझे चाहिए उससे मेल खाता है, और मूल्य को प्रतिस्थापित करता है।

हास्केल में मैं अनुमान लगा रहा हूं कि मुझे अपने पेड़ की प्रतिलिपि बनाने में सक्षम होना चाहिए, लेकिन आप रचनाकारों और विभिन्न डेटा प्रकारों में दफन की गई सूचियों से कैसे निपटते हैं?

उत्तर

9

आप इसके लिए सामान्य प्रोग्रामिंग का उपयोग कर सकते हैं।

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

{-# LANGUAGE DeriveDataTypeable #-} 

आयात मॉड्यूल Data.Generics: अपने मॉड्यूल के बहुत शीर्ष पर, लेखन द्वारा आपका बॉयलरप्लेट स्क्रैप सक्षम करें। इसके बाद Show के अलावा, अपने डेटाटाइप के लिए Typeable और Data उदाहरण भी प्राप्त करें।

toFoo :: Data a => a -> a 
toFoo = everywhere (mkT step) 
    where 
    step (DataA2 _) = DataA2 "foo" 
    step x   = x 

सब आप इस काम करने के लिए क्या करने की जरूरत है कि:

अब आप समारोह आप इस तरह का अनुरोध लिख सकते हैं। उदाहरण के लिए, जब आप toFoo [DataA1 [], DataA2 "hi", DataA3 "yo" []] पर कॉल करते हैं, तो उत्तर [DataA1 [],DataA2 "foo",DataA3 "yo" []] है।

आशा है कि इससे मदद मिलती है!

+2

धन्यवाद। यह वही है जिसे मैं देख रहा था। इसे काम करने के लिए, मुझे डेटा आयात करना पड़ा। जेनरिक्स – Chris

+0

आप सही हैं, मेरा बुरा! मेरा जवाब तय किया। धन्यवाद। – Martijn

+1

शानदार। यहां किसी को खुशी है कि एसआईबी काम कैसे करें। +1 –

2

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

type SFilter = String -> String 

-- to filter a tree, say how A2, A3, B2, and B3 should be changed 

type Filter tree = SFilter -> SFilter -> SFilter -> SFilter -> (tree -> tree) 

afilter :: Filter DataA 
bfilter :: Filter DataB 
tfilter :: Filter Tree 

tfilter a2 a3 b2 b3 = map (afilter a2 a3 b2 b3) 
afilter a2 a3 b2 b3 = fil 
    where fil (DataA1 bs) = DataA1 $ map (bfilter a2 a3 b2 b3) bs 
     fil (DataA2 s) = DataA2 (a2 s) 
     fil (DataA3 s as) = DataA3 (a3 s) (map fil as) 

bfilter a2 a3 b2 b3 = fil 
    where fil (DataB1 as) = DataB1 $ map (afilter a2 a3 b2 b3) as 
     fil (DataB2 s) = DataB2 (b2 s) 
     fil (DataB3 s bs) = DataB3 (b3 s) (map fil bs) 
+1

दिलचस्प। उदाहरण सामने आया है क्योंकि वास्तविक पेड़ जो मैं साथ काम कर रहा हूं वह पारसी का उपयोग कर एक भाषा के लिए एक सार पेड़ है। तो आप अभिव्यक्तियों में अभिव्यक्ति प्राप्त करते हैं और हर तरह की अन्य अजीब मोड़ जो आप सोच सकते हैं। तो आप कह रहे हैं कि प्रत्येक ऑब्जेक्ट के लिए एक एसफिल्टर बनाएं जिसे मैं कुशल बनाना चाहता हूं। एकमात्र मुद्दा जो मैं देखता हूं वह यह है कि कई प्रकार के साथ एक्यूटुअल पेड़ काफी बड़ा होता है। यह वास्तव में एक अच्छा उदाहरण है जो मुझे लगता है कि मैं काम कर सकता हूं। – Chris

+0

@ क्रिस: यदि आपके पेड़ के प्रकार पारस्परिक रूप से रिकर्सिव हैं, स्थिर प्रकार प्रणाली के कारण प्रत्येक प्रकार के लिए फ़ंक्शन के आसपास कोई रास्ता नहीं है। मैंने टाइप क्लास का उपयोग करके इस तरह की जटिलता में से कुछ को प्रबंधित किया है। या यदि आप वास्तव में बहादुर हैं, तो आप अब लोगों या जेनिक्स के लिए अपने बॉयलरप्लेट या राल्फ हिनज के जेनेरिक प्रोग्रामिंग को स्क्रैप करने का प्रयास कर सकते हैं। –

+1

हां, ऐसा लगता है कि आप जिस कीवर्ड को खोज रहे हैं वह "जेनेरिक प्रोग्रामिंग" है। –

2

आप संपूर्ण डेटा संरचना को पार करना चाहते हैं और यहां कुछ आइटम बदलना चाहते हैं। यह आमतौर पर एक फ़ंक्शन द्वारा किया जाता है जो डेटा संरचना को पैरामीटर के रूप में लेता है और संरचना का नया, परिवर्तित संस्करण देता है।

इनपुट के हर मामले के लिए यह फ़ंक्शन परिभाषित करता है कि नया मान वापस लौटाया जाना चाहिए जैसा दिखता है।

मूल कार्य जो Tree संशोधित करता है (जो केवल DataA मानों की एक सूची है) शायद संशोधित मानों की एक नई सूची वापस करनी चाहिए। हम एक modifyA कार्य करने के लिए मूल्यों की उन संशोधनों को स्थगित करते हैं, तो मुख्य संशोधन समारोह इस तरह दिखता है:

-- # function to change a |Tree| 
mutate :: Tree -> Tree 
mutate as = map mutateA as 
    -- # (The |map| function applies the |mutateA| function to every 
    -- # element of |as|, creating a list of all the return values) 

अब mutateA समारोह के लिए सभी संभव DataA मूल्यों को बदलने के लिए परिभाषित करने की आवश्यकता है, और यह है सबसे अच्छा mutateB फ़ंक्शन के साथ जो DataB मानों को संभालता है।

इन कार्यों मूल्यों के विभिन्न संभव मामलों को देखो और उचित नया मान:

-- # function to change |DataA| items 
mutateA :: DataA -> DataA 
    -- # A |DataA1| is a |DataA1| with modified values 
mutateA (DataA1 bs) = DataA1 (map mutateB bs) 
    -- # A |DataA3| is a |DataA3| with modified values 
mutateA (DataA3 s as) = DataA3 s (map mutateA as) 
    -- # In the remaining case(s) the value stays the same 
mutateA d    = d 

-- # function to change |DataB| items 
mutateB :: DataB -> DataB 
mutateB (DataB1 as) = DataB1 (map mutateA as) 
mutateB (DataB3 s bs) = DataB3 s (map mutateB bs) 
    -- # Here comes a real change 
mutateB (DataB2 _) = DataB2 "foo" 

एक नए तत्व गणना की जाती है, जहां सभी DataB2 मूल्यों कहीं भी पेड़ में प्रत्येक तत्व के लिए इस तरह पेड़ में "foo" द्वारा प्रतिस्थापित किया जाता है।

यह अपेक्षाकृत वर्बोज़ है क्योंकि आपके पास पांच अलग-अलग मामले हैं जिनमें की सूची शामिल है जिन्हें चलने की आवश्यकता है, लेकिन यह हास्केल के लिए विशिष्ट नहीं है। में एक अनिवार्य भाषा है जिसमें आपके पास आमतौर पर पांच की जगह map पर लूप के लिए पांच होगी।

शायद आप इस "ओवरहेड" को कम करने के लिए अपनी डेटा संरचना को सरल बना सकते हैं।यह निश्चित रूप से आपके वास्तविक उपयोग मामले पर निर्भर करता है, लेकिन शायद, उदाहरण के लिए, आपको Data2 मामलों की आवश्यकता नहीं है: क्या DataA2 "abc" और DataA3 "abc" [] के बीच कोई अंतर है?

+2

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

0

आप पारस्परिक रूप से रिकर्सिव डेटा प्रकारों के साथ काम करने के लिए multirec लाइब्रेरी पर एक नज़र डालना चाहते हैं। मैंने इसका उपयोग नहीं किया है, लेकिन आपने जो वर्णन किया है उससे यह लगता है कि इसका उद्देश्य यह है कि आप किस प्रकार की समस्या के साथ काम कर रहे हैं। यह जेनेरिक प्रोग्रामिंग का उपयोग करता है जैसे कि अन्य उत्तरों ने सुझाव दिया है, लेकिन आपको अपने आप को सबकुछ लागू करने का समय बचा सकता है।