2010-03-02 32 views
15

मैं सिस्टम विश्लेषण को फिर से सीखने की कोशिश कर रहा हूं। मुझे बहुत ऑब्जेक्ट उन्मुख सोच मिली है जिसके लिए मैं अभी भी हास्केल में समकक्ष नहीं ढूंढ पा रहा हूं।हास्केल में ऑब्जेक्ट ग्राफ़ कैसे प्रबंधित करते हैं?

एक काल्पनिक प्रणाली में एम्बुलेंस स्टेशन, एम्बुलेंस और क्रू शामिल हैं। (यह पहले से ऑब्जेक्ट-वाई प्राप्त कर रहा है।) इस सभी राज्य को एक बड़े सिस्टमस्टेट प्रकार में लपेटा जा सकता है। सिस्टमस्टेट [स्टेशन] [एम्बुलेंस] [क्रू]। मैं फिर उन कार्यों को बना सकता हूं जो सिस्टमस्टेट लेते हैं, और एक नया सिस्टमस्टेट लौटाते हैं।

module AmbSys 
    (version 
    , SystemState 
    , Station 
    , Ambulance 
    , Crew 
    ) where 

version = "0.0.1" 

data SystemState = SystemState [Station] [Ambulance] [Crew] deriving (Show) 

data Station = Station { stName :: String 
         , stAmbulances :: [Ambulance] 
         } deriving (Show) 

data Ambulance = Ambulance { amCallSign :: String 
          , amStation :: Station 
          , amCrew :: [Crew] 
          } deriving (Show) 

data Crew = Crew { crName :: String 
       , crAmbulance :: Ambulance 
       , crOnDuty :: Bool 
       } deriving (Show) 

यहाँ एक GHCi सत्र मैं कुछ डेटा को बना है।

*AmbSys> :load AmbSys     
[1 of 1] Compiling AmbSys   (AmbSys.hs, interpreted) 
Ok, modules loaded: AmbSys. 
*AmbSys> let s = Station "London" []     
*AmbSys> let a = Ambulance "ABC" s []    
*AmbSys> let s' = Station "London" [a] 
*AmbSys> let c = Crew "John Smith" a False   
*AmbSys> let a' = Ambulance "ABC" s [c] 
*AmbSys> let s'' = Station "London" [a']    
*AmbSys> let system_state = SystemState [s''] [a'] [c] 
*AmbSys> system_state         
SystemState [Station {stName = "London", stAmbulances = [Ambulance {amCallSign = "ABC", 
amStation = Station {stName = "London", stAmbulances = []}, amCrew = [Crew 
{crName = "John Smith", crAmbulance = Ambulance {amCallSign = "ABC", 
amStation = Station {stName = "London", stAmbulances = []}, amCrew = []}, 
crOnDuty = False}]}]}] [Ambulance {amCallSign = "ABC", amStation = Station { 
stName = "London", stAmbulances = []}, amCrew = [Crew {crName = "John Smith", 
crAmbulance = Ambulance {amCallSign = "ABC", amStation = Station {stName = "London", 
stAmbulances = []}, amCrew = []}, crOnDuty = False}]}] [Crew {crName = "John Smith", 
crAmbulance = Ambulance {amCallSign = "ABC", amStation = Station {stName = "London", 
stAmbulances = []}, amCrew = []}, crOnDuty = False}] 

आप पहले से ही समस्याओं की एक जोड़ी यहाँ देख सकते हैं:

  1. मैं एक सुसंगत SystemState बनाने में असमर्थ किया गया है - मूल्यों के कुछ ऐसे s या s के रूप में 'पुराने' मान के लिए 'कर रहे हैं, एस 'के बजाय।
  2. 'समान' डेटा के कई संदर्भों में अलग-अलग प्रतियां हैं।

अब मैं एक समारोह जो एक SystemState और एक क्रू सदस्य के नाम जो एक नई SystemState जहां कि चालक दल के सदस्य 'ऑफ ड्यूटी' है रिटर्न लेता है बना सकते हैं।

मेरी समस्या यह है कि मुझे एम्बुलेंस में चालक दल के सदस्य और सिस्टमस्टेट में क्रू सदस्य की समान प्रतिलिपि को ढूंढना और बदलना है।

यह छोटे सिस्टम के लिए संभव है, लेकिन वास्तविक प्रणालियों में कई और संबंध हैं। यह एक एन-स्क्वायर समस्या की तरह दिखता है।

मुझे बहुत पता है कि मैं किसी ऑब्जेक्ट उन्मुख तरीके से सिस्टम के बारे में सोच रहा हूं।

हास्केल में ऐसी प्रणाली को सही ढंग से कैसे बनाया जाएगा?

संपादित करें: अपने जवाब के लिए हर किसी के लिए धन्यवाद, और reddit पर उन बहुत http://www.reddit.com/r/haskell/comments/b87sc/how_do_you_manage_an_object_graph_in_haskell/

मेरे समझ अब है कि मैं चीजों को मैं हास्केल में चाहते हो सकता है लगता है। नकारात्मक पक्ष में, ऐसा लगता है कि संदर्भों की आवश्यक कमी के कारण, ऑब्जेक्ट/रिकॉर्ड/स्ट्रक्चर ग्राफ़ हास्केल में 'प्रथम श्रेणी' ऑब्जेक्ट्स नहीं हैं (क्योंकि वे सी/जावा/आदि में हैं)। वहाँ सिर्फ एक व्यापार बंद है - कुछ कार्य हास्केल में वाक्य रचना सरल कर रहे हैं, कुछ सी में सरल (और अधिक असुरक्षित) कर रहे हैं

उत्तर

8

छोटे टिप: आप का उपयोग करते हैं एक पुनरावर्ती let या where (एक .hs फ़ाइल में, मैं ' टी लगता है कि यह GHCi में काम करता है) आप कम से कम और अधिक आसानी से प्रारंभिक ग्राफ सेट कर सकते हैं इस प्रकार है:

ambSys = SystemState [s] [a] [c] where 
    s = Station "London" [a] 
    a = Ambulance "ABC" s [c] 
    c = Crew "John Smith" a False 

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

संपादित करें: मैं कुछ और इसके बारे में सोचा है, और यहाँ क्या मैं शायद ऐसा करेंगे:

मैं कुंजियों का उपयोग करके वस्तु ग्राफ में चक्र टूट जाएगा।कुछ इस तरह काम करेगा (जब असली रेखांकन के निर्माण मैं एक समान दृष्टिकोण का इस्तेमाल किया):

import qualified Data.Map as M 

version = "0.0.1" 

newtype StationKey = StationKey String deriving (Eq,Ord,Show) 
newtype AmbulanceKey = AmbulanceKey String deriving (Eq,Ord,Show) 
newtype CrewKey = CrewKey String deriving (Eq,Ord,Show) 

data SystemState = SystemState (M.Map StationKey Station) (M.Map AmbulanceKey Ambulance) (M.Map CrewKey Crew) deriving (Show) 

data Station = Station { stName :: StationKey 
         , stAmbulances :: [AmbulanceKey] 
         } deriving (Show) 

data Ambulance = Ambulance { amCallSign :: AmbulanceKey 
          , amStation :: StationKey 
          , amCrew :: [CrewKey] 
          } deriving (Show) 

data Crew = Crew { crName :: CrewKey 
       , crAmbulance :: AmbulanceKey 
       , crOnDuty :: Bool 
       } deriving (Show) 

ambSys = SystemState (M.fromList [(londonKey, london)]) (M.fromList [(abcKey, abc)]) (M.fromList [(johnSmithKey, johnSmith)]) where 
    londonKey = StationKey "London" 
    london = Station londonKey [abcKey] 
    abcKey = AmbulanceKey "ABC" 
    abc = Ambulance abcKey londonKey [johnSmithKey] 
    johnSmithKey = CrewKey "John Smith" 
    johnSmith = Crew johnSmithKey abcKey False 

और फिर आप अपने खुद के राज्य में सुधार करने वाले combinators को परिभाषित करने शुरू कर सकते हैं। जैसा कि आप देख सकते हैं, राज्यों का निर्माण अब अधिक वर्बोज़ है, लेकिन show आईएनजी अच्छी तरह से काम करता है!

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

+0

धन्यवाद, जो आकस्मिक समस्याओं में से एक को बहुत अच्छी तरह से हल करता है। – fadedbee

+1

मैं "कुंजी" को "न्यूटाइप कुंजी ए = कुंजी स्ट्रिंग व्युत्पन्न (ईक, ऑर्ड, शो)" के रूप में परिभाषित करता हूं। यह सिर्फ तीन अलग-अलग कुंजी प्रकारों के बीच पुनरावृत्ति की एक छोटी राशि बचाता है। –

1

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

+0

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

1

मैंने इस तरह की चीज भी करने की कोशिश की है और निष्कर्ष मैं पहुंचा था कि हास्केल (मेरे लिए) शायद नौकरी के लिए सही उपकरण नहीं था। 'एक ही' डेटा अलग प्रतियां के सन्दर्भ की

बहुत सारे:

आपका प्रश्न 2 उस पर हो जाता है।

हास्केल, एक भाषा के रूप में, विशेष रूप से यह मुश्किल के लिए "शेयर उदाहरणों" बनाने या "अलग प्रतियां" बनाने के लिए बनाया गया है। चूंकि सभी चरों में अपरिवर्तनीय मान होते हैं, इसलिए पहचान के लिए तुलना करने के लिए ऑब्जेक्ट्स का कोई संदर्भ नहीं होता है।

उसने कहा, कुछ तकनीकें हैं।

एक तकनीक अपने संरचनाओं के लिए mutable objects का उपयोग करना है। हालांकि, यह आपके सभी कोड को एक मोनड में मजबूर कर देगा।

आप इस पेपर को भी देख सकते हैं Type-Safe Observable Sharing जो दिखाता है कि ग्राफ बनाने में निम्न-स्तर संदर्भों का समर्थन करने वाली कुछ नई भाषा सुविधाओं का उपयोग कैसे किया जाए। उनका उदाहरण डिजिटल सर्किट है, लेकिन मुझे लगता है कि यह सामान्यीकृत है।

5

जब तक आप विरासत और उपप्रकार बहुरूपता के बारे में बात करना शुरू नहीं करते हैं तब तक ऑब्जेक्ट-ओरिएंटेड-वाई नहीं मिल रहा है। कार्यक्रमों में ओओ की कल्पना होने से पहले "एम्बुलेंस" और "स्टेशन" नामक डेटा संरचनाएं शामिल थीं; ओओ डेटा अबास्ट्रक्शन और encapsulation पर कोई एकाधिकार नहीं है। एफपी डिजाइन भी "डोमेन संचालित" होगा, जैसा अनिवार्य प्रोग्रामिंग होगा।

आ रही समस्या का कैसे राज्य का प्रबंधन करने, और यह Haskell (वास्तव में एक पुरानी समस्या है, किसी भी प्रोग्रामिंग प्रणाली में, SICP की धारा 3.1.3 (Abelson और Sussman की संरचना और कंप्यूटर प्रोग्राम http://mitpress.mit.edu/sicp/ की व्याख्या देखना है (बड़े, अकादमिक शब्दों, न ही डोमेन नाम से बाहर मत डालो, यह बहुत पठनीय है - उनका उदाहरण एक बैंक खाता है)

आपकी समस्या यह है कि आप पुरानी, ​​पुरानी स्थिति को संदर्भित और पकड़ रहे हैं मैं सुझाव दूंगा कि आप ऐसे कार्य लिखें जो वर्तमान स्थिति लेते हैं, इसे संशोधित करते हैं, और एक नया राज्य लौटाते हैं। कुछ:

addStation state station = 
    let (SystemState stations ambs crews) = state 
    in SystemState (station:stations) ambs crews) 

और यदि आप ghci दुभाषिया का उपयोग कर रहे हैं, तो यह चर चर के बारे में जानना आसान होगा, जिसमें अंतिम गणना का परिणाम शामिल है।

आप अंततः राज्य इकाई पर पहुंच जाएंगे, लेकिन यह लग रहा है कि बाद में जैसे ....

2

हास्केल प्रणाली की तरह आप का वर्णन कर रहे हैं मॉडलिंग के लिए एक शानदार विकल्प है।

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

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

लेकिन यहां मुख्य मुद्दा यह है कि जीएचसीआई का प्रभावी ढंग से उपयोग कैसे करें।

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

फिर भी, उन तीनों उपयोगों में से प्रत्येक के लिए, मुझे डेटा संरचनाओं की आवश्यकता है। आमतौर पर मुझे जो चाहिए वह इतना आसान है कि मैं पूरी चीज़ को एक ही बार टाइप कर सकता हूं। उन्हें वास्तव में के लिए बहुत आसान नहीं होना चाहिए - यह भूलें कि आप एकाधिक पारस्परिक-पुनरावर्ती एक में परिभाषाओं को कथन को ';', और से अलग करके बता सकते हैं कि जीएचसीआई बहु- ": {" और ":} के साथ लाइन बयान। 'आदेशों

डेटा संरचना की आवश्यकता काफी जटिल है कि मैं संवर्द्धित इसे बनाने अप करना चाहते हैं आप की तरह कर रहे थे, वहाँ कई आसान तरीके करने के लिए कर रहे हैं कि

एक परिवर्तनीय चर प्राप्त करने के लिए जिसे आप बनाने के लिए बार-बार संशोधित करते हैं, वैसे ही आप इसे पर एक अनिवार्य भाषा के लिए कमांड लाइन प्रॉम्प्ट पर करेंगे, Data.Ioref मॉड्यूल को देखें। यदि आप हास्केल के लिए नए हैं, तो मैं डेटा से बचने की अनुशंसा करता हूं। आपके प्रोग्रामिंग में प्लेग की तरह IORef - यह हमेशा आपको लुभाएगा और यह लगभग हमेशा गलत बात करने के लिए है। लेकिन जीएचसीआई प्रॉम्प्ट पर, यह ठीक है।

सच में, मैं लगभग कभी ऐसा नहीं करता हूं। आलसी होने के नाते, मैं अप-तीर और अन्य कमांड-लाइन संपादन कुंजी का उपयोग पूरी चीज को एक GHCi कमांड में बढ़ने के लिए उपयोग करता हूं।

और निश्चित रूप से, अगर डेटा संरचना लिखते-लिखते वास्तव में सार्थक और नहीं एक थ्रो-दूर उदाहरण है, आप अपने पसंदीदा हास्केल के बजाय संपादक प्रॉम्प्ट पर में एक फ़ाइल में टाइप करने के लिए चाहता हूँ। फिर आप अपने संपादक के जीएचसीआई एकीकरण, या जीएचसीआई के "आर" कमांड का उपयोग करेंगे, ताकि आपके संरचना जीएचसीआई में उपलब्ध हो।

+0

ghci का उपयोग करने के लिए कोई विशेष कारण नहीं था, यह प्रयोग करते समय मैं उपयोग करता हूं। एक समारोह का एक उदाहरण मैं एक एम्बुलेंस से दूसरे में एक चालक दल के सदस्य को स्थानांतरित करने के लिए एक समारोह था। यह चालक दल के रिकॉर्ड में एम्बुलेंस और एम्बुलेंस क्षेत्र दोनों में चालक दल की सूचियों को प्रभावित करेगा। सिस्टमस्टेट का उद्देश्य ऐसा कुछ था जो इस तरह के फ़ंक्शन के पैरामीटर के रूप में प्रदान किया जा सकता था। – fadedbee

1

यदि आपको वास्तव में, वास्तव में डेटा को रिकर्सिव होने की आवश्यकता है, तो उचित ग्राफ लाइब्रेरी का उपयोग करें जैसे fgl।

3

एक विकल्प जो दूसरों ने दिया है वह एक अलग कुंजी प्रकार का उपयोग करने की क्षमता है, और संभवतः क्रू सदस्यों, स्टेशनों या एम्बुलेंस के मानचित्र में महत्वपूर्ण रूप से परिपत्र संदर्भों को देखने के लिए मूल्यों को देखें।

जो और अधिक क्या आप के लिए उपयोग किया जाता है की तरह बर्ताव अधिक प्रत्यक्ष एन्कोडिंग संदर्भों का उपयोग, निश्चित रूप से नहीं है:

data Station = Station { stName :: String 
         , stAmbulances :: [IORef Ambulance] 
         } deriving (Show) 

data Ambulance = Ambulance { amCallSign :: String 
          , amStation :: IORef Station 
          , amCrew :: [IORef Crew] 
          } deriving (Show) 

data Crew = Crew { crName :: String 
       , crAmbulance :: IORef Ambulance 
       , crOnDuty :: Bool 
       } deriving (Show) 

यह एक भारी पक्ष effectful प्रोग्रामिंग शैली का परिणाम है। संक्षेप में आप आईओ मोनैड का उपयोग करके, हास्केल में सी/सी ++ लिखना शुरू करते हैं।

इसे हल करने के लिए दो हास्केल-जैसे दृष्टिकोण हैं।

एक गाँठ बांधना है, और परिपत्र संदर्भ रखना है, लेकिन फिर अद्यतन समस्याग्रस्त हो जाता है।

data Station = Station { stName :: String 
         , stAmbulances :: [Ambulance] 
         } deriving (Show) 

data Ambulance = Ambulance { amCallSign :: String 
          , amCrew :: [Crew] 
          } deriving (Show) 

data Crew = Crew { crName :: String 
       , crOnDuty :: Bool 
       } deriving (Show) 

आप स्टेशन से चालक दल पहुंच सकता है::

अन्य वृत्तीय संदर्भ को मारने के लिए है

stCrew :: Station -> [Crew] 
stCrew = stAmbulances >>= amCrew 

पहुँच किस प्रकार की आवश्यकता होगी पर निर्भर करता है, यह एक आवश्यकता हो सकती है क्रू के एक सदस्य तक पहुंचने के लिए धीमा रास्ता।

लेकिन, आपकी सोच से वस्तुओं को पूरी तरह से खत्म करने के लिए एक बेहतर एन्कोडिंग भी हो सकती है, और उस मानचित्र को गले लगाओ जिसे आप स्वयं संरचना के हिस्से के रूप में कुंजी देखने के लिए उपयोग करेंगे। मैं इस कोड की किसी न किसी प्रकृति के लिए क्षमा चाहता हूं, मैं इसे एक साथ लिख रहा हूं।

import Control.Monad ((>=>)) 
import Data.Map (Map) 
import qualified Data.Map as Map 

type Name = String 
newtype CrewTraits = CrewTraits { onDuty :: Bool } 
type Crew = (Name, CrewTraits) 

type CallSign = String 
type AmbulanceTraits = Map Name AssignmentTraits 
type Amulance = (CallSign, AmbulanceTraits) 

type StationName = String 
type StationTraits = Map CallSign AmbulanceTraits 
type Station = (StationName,StationTraits) 

type Fleet = Map StationName StationTraits 

crew :: Name -> Bool -> Crew 
crew name isOnDuty = (name, CrewTraits isOnDuty) 

ambulance :: CallSign -> [Crew] -> Ambulance 
ambulance sign crew = (sign, Map.fromList crew) 

station :: StationName -> [Ambulance] -> Station 
station name ambulances = (name, Map.fromList ambulances) 

fleet :: [Station] -> Fleet 
fleet = Map.fromList 

अब तुम सिर्फ Data.Map से निर्मित कार्यक्षमता का उपयोग करके एक स्टेशन बदल सकते हैं:

updateStationTraits :: (StationName -> StationTraits -> Maybe StationTraits) -> 
         StationName -> Fleet -> Fleet 
updateStationTraits = Map.updateWithKey 

जो आप का नाम और StationTraits tupling द्वारा एक छोटे से अधिक प्राकृतिक लग बना सकता है:

updateStation :: (Station -> Maybe StationTraits) -> 
       StationName -> Fleet -> Fleet 
updateStation = Map.updateWithKey . curry 

addAmbulanceToFleet :: Ambulance -> StationName -> Fleet -> Fleet 
addAmbulanceToFleet (k,v) = Map.adjust (Map.insert k v) 

इसके साथ आप अब इस संरचना में एक पथ की धारणा को एक कुंजी की पूर्व धारणा के साथ एकजुट कर सकते हैं:

type CrewPath = (StationName,CallSign,Name) 
type AmbulancePath = (StationName, CallSign) 
type StationPath = StationName 

lookupCrewTraits :: CrewKey -> Fleet -> Maybe CrewTraits 
lookupCrewTraits (s,c,n) = lookup s >=> lookup c >=> lookup n 

lookupCrew :: CrewKey -> Fleet -> Maybe Crew 
lookupCrew [email protected](_,_,n) = (,) n `fmap` lookupCrewTraits scn 
+0

यह दिलचस्प लग रहा है। आपने जो कुछ लिखा है वह हैस्केल की मेरी वर्तमान समझ से परे है, लेकिन यह एक अच्छी बात है। – fadedbee

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