2011-10-16 27 views
37
data Plane = Plane { point :: Point, normal :: Vector Double } 
data Sphere = Sphere { center :: Point, radius :: Double } 

class Shape s where 
    intersect :: s -> Ray -> Maybe Point 
    surfaceNormal :: s -> Point -> Vector Double 

मैंने Plane और SphereShape के उदाहरण भी बनाए हैं।विभिन्न प्रकार की सूची?

मैं एक ही सूची में गोलाकारों और विमानों को स्टोर करने की कोशिश कर रहा हूं, लेकिन यह काम नहीं करता है। मैं समझता हूं कि यह काम नहीं करना चाहिए क्योंकि Sphere और Plane दो अलग-अलग प्रकार हैं, लेकिन वे Shape दोनों उदाहरण हैं, तो क्या यह काम नहीं करना चाहिए? मैं सूची में आकार और विमान कैसे स्टोर करूं?

shapes :: (Shape t) => [t] 
shapes = [ Sphere { center = Point [0, 0, 0], radius = 2.0 }, 
     Plane { point = Point [1, 2, 1], normal = 3 |> [0.5, 0.6, 0.2] } 
     ] 
+1

http://www.haskell.org/haskellwiki/Heterogenous_collections –

+2

मुझे विषम संग्रहों से अवगत था, लेकिन यह ऐसा कुछ है जिसे मैं टालना चाहता था। – Arlen

उत्तर

47

यह समस्या ऑब्जेक्ट उन्मुख और कार्यात्मक सोच के बीच एक मोड़ बिंदु का प्रतिनिधित्व करती है। कभी-कभी परिष्कृत हास्केलर भी इस मानसिक संक्रमण में हैं, और उनके डिजाइन अक्सर थॉमस के उत्तर में वर्णित existential typeclass पैटर्न में आते हैं।

इस समस्या का एक कार्यात्मक समाधान एक डेटा प्रकार (आमतौर पर एक बार यह हो जाए, typeclass के लिए की जरूरत गायब हो जाती है) में typeclass reifying शामिल है:

data Shape = Shape { 
    intersect :: Ray -> Maybe Point, 
    surfaceNormal :: Point -> Vector Double 
} 

अब आप आसानी से Shape रों की सूची का निर्माण कर सकते हैं , क्योंकि यह एक monomorphic प्रकार है। चूंकि हास्केल डाउनकास्टिंग का समर्थन नहीं करता है, Plane एस और Sphere एस के बीच प्रतिनिधित्व भेद को हटाकर कोई जानकारी खो जाती है। ,

plane :: Point -> Vector Double -> Shape 
sphere :: Point -> Double -> Shape 

आप सब कुछ आप Shape डेटा प्रकार में एक आकार के बारे में पता करने की जरूरत पर कब्जा नहीं कर सकते हैं, तो आप एक बीजीय डेटा प्रकार के साथ मामलों की गणना कर सकते हैं के रूप में थॉमस का सुझाव दिया: विशिष्ट डेटा प्रकार कार्यों कि निर्माण Shape रों बन । लेकिन यदि संभव हो तो मैं इसके खिलाफ सिफारिश करूंगा; इसके बजाय, उदाहरणों को सूचीबद्ध करने की बजाय आपको आवश्यक आकार की आवश्यक विशेषताओं को खोजने का प्रयास करें।

+4

यह अच्छी तरह से कहा गया है कि हमें टाइप क्लास को ओओ विरासत या इंटरफेस के रूप में नहीं सोचना चाहिए और उनके बारे में सोचना चाहिए। वे केवल फ़ंक्शंस का एक सेट हैं जो एक प्रकार – Ankur

+0

का समर्थन कर सकता है यदि आपको टाइप करने की आवश्यकता है कि क्या कोई विमान या क्षेत्र था या नहीं? तो क्या आप एडीटी शैली का सुझाव देंगे? – CMCDragonkai

+0

@CMCDragonkai, मैं शायद एक 'डेटा क्षेत्र' बनाउंगा जिसमें एक क्षेत्र के आवश्यक गुण हैं, 'डेटा प्लेन' जिसमें विमान की आवश्यक गुण हैं, और फिर 'toShape :: क्षेत्र -> आकार' जैसे इंजेक्शन फ़ंक्शन हैं। । हो सकता है कि इसे 'क्लास टूशिप' (एक बाद के विचार के रूप में) में रखें – luqui

24

आप एक विषम सूची है, जो सबसे Haskellers विशेष रूप से नहीं है, भले ही वे खुद को इस एक ही सवाल जब पहली हास्केल सीखने को कहा है की तरह देख रहे हैं।

आप लिखें:

shapes :: (Shape t) => [t] 

यह कहते सूची टाइप t, जिनमें से सभी समान हैं और एक आकार होने के लिए होता है (एक ही आकार!)। दूसरे शब्दों में - नहीं, यह काम नहीं करना चाहिए कि आपके पास यह कैसे है।

दो आम तरीके इसे संभाल करने के लिए (पहले एक हास्केल 98 तरीका है, तो एक सजावटी तरह से है कि मैं दूसरी अनुशंसा नहीं करते) हैं:

एक नए प्रकार को स्थिर संघ ब्याज के उपप्रकार का उपयोग करें:

data Foo = F deriving Show 
data Bar = B deriving Show 

data Contain = CFoo Foo | CBar Bar deriving Show 
stuffExplicit :: [Contain] 
stuffExplicit = [CFoo F, CBar B] 

main = print stuffExplicit 

यह अच्छा दिख रहा है क्योंकि यह सीधे आगे है और आप सूची में मौजूद चीज़ों के बारे में कोई जानकारी नहीं खोते हैं। आप निर्धारित कर सकते हैं कि पहला तत्व Foo है और दूसरा तत्व Bar है। जैसा कि आप शायद पहले ही महसूस कर चुके हैं, यह है कि आपको एक नया Contain प्रकार कन्स्ट्रक्टर बनाकर प्रत्येक घटक प्रकार को स्पष्ट रूप से जोड़ना होगा। अगर यह अवांछनीय है तो पढ़ना जारी रखें।

मौजूदा प्रकारों का उपयोग करें: एक अन्य समाधान में तत्वों के बारे में जानकारी खोना शामिल है - आप केवल एक ही कक्षा में हैं, यह समझते हुए कहते हैं कि तत्व एक वर्ग में हैं। नतीजतन आप सूची तत्वों पर उस वर्ग से केवल संचालन का उपयोग कर सकते हैं। उदाहरण के लिए, नीचे ही याद होगा तत्वों Show वर्ग के हैं, इसलिए केवल एक चीज आप तत्वों के लिए क्या कर सकते हैं उपयोग कार्यों कि Show में बहुरूपी हैं:

data AnyShow = forall s. Show s => AS s 

showIt (AS s) = show s 

stuffAnyShow :: [AnyShow] 
stuffAnyShow = [AS F, AS B] 

main = print (map showIt stuffAnyShow) 

यह हास्केल भाषा के लिए कुछ एक्सटेंशन की आवश्यकता है , अर्थात् ExplicitForAll और ExistentialQuantification। हमें showIt स्पष्ट रूप से परिभाषित करना था (AnyShow प्रकार को डीकनस्ट्रक्चर करने के लिए पैटर्न मिलान का उपयोग करना) क्योंकि आप अस्तित्वत्मक मात्रा का उपयोग करने वाले डेटा प्रकारों के लिए फ़ील्ड नामों का उपयोग नहीं कर सकते हैं।

और अधिक समाधान हैं (उम्मीद है कि कोई अन्य जवाब Data.Dynamic का उपयोग करेगा - यदि कोई भी नहीं करता है और आप रुचि रखते हैं तो उस पर पढ़ लें और जेनरेट करने वाले किसी भी प्रश्न पोस्ट करने के लिए स्वतंत्र महसूस करें)।

+0

पहली विधि मैंने कोशिश की पहली चीज़ है, और यह अच्छी तरह से काम नहीं किया। मुझे यकीन नहीं है कि मुझे दूसरी विधि पसंद है! मैं एक्सटेंशन का उपयोग करने से बचना चाहूंगा। – Arlen

+0

मैं इस पर उत्सुक नहीं हूं - यह अधिक विदेशी वाक्यविन्यास जैसा लगता है जब अवधारणाएं पहले से ही (संभवतः) पाठक के लिए पर्याप्त विदेशी हैं। शायद एक और जवाब में? –

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