2013-03-18 7 views
53

में जटिल स्थिति को बनाए रखना मान लीजिए कि आप हास्केल में काफी बड़े सिमुलेशन का निर्माण कर रहे हैं। कई अलग-अलग प्रकार की संस्थाएं हैं जिनके गुण सिमुलेशन के रूप में अपडेट होते हैं। आइए, उदाहरण के लिए, कि आपकी संस्थाओं को बंदर, हाथी, भालू, आदि कहा जाता है ..हास्केल

इन संस्थाओं के राज्यों को बनाए रखने के लिए आपकी पसंदीदा विधि क्या है?

पहली और सबसे स्पष्ट दृष्टिकोण मैं के बारे में सोचा था इस:

mainLoop :: [Monkey] -> [Elephant] -> [Bear] -> String 
mainLoop monkeys elephants bears = 
    let monkeys' = updateMonkeys monkeys 
     elephants' = updateElephants elephants 
     bears'  = updateBears  bears 
    in 
    if shouldExit monkeys elephants bears then "Done" else 
     mainLoop monkeys' elephants' bears' 

यह पहले से ही बदसूरत इकाई के प्रत्येक प्रकार के स्पष्ट रूप से mainLoop समारोह हस्ताक्षर में उल्लेख होने है। अगर आप 20 प्रकार की इकाइयां कहें तो आप कल्पना कर सकते हैं कि यह बिल्कुल भयानक कैसे होगा। (20 जटिल सिमुलेशन के लिए अनुचित नहीं है।) तो मुझे लगता है कि यह एक अस्वीकार्य दृष्टिकोण है। लेकिन इसकी बचत कृपा यह है कि updateMonkeys जैसे कार्यों में वे बहुत स्पष्ट हैं: वे बंदरों की एक सूची लेते हैं और एक नया लौटते हैं।

तो फिर अगले सोचा एक बड़ा डेटा संरचना है कि सभी राज्य धारण में सब कुछ रोल होगा, इस प्रकार mainLoop के हस्ताक्षर की सफाई:

mainLoop :: GameState -> String 
mainLoop gs0 = 
    let gs1 = updateMonkeys gs0 
     gs2 = updateElephants gs1 
     gs3 = updateBears  gs2 
    in 
    if shouldExit gs0 then "Done" else 
     mainLoop gs3 

कुछ सुझाव है कि हम GameState एक राज्य में लपेट do में मोनाड और updateMonkeys आदि पर कॉल करें। कोई बात नहीं। कुछ सुझाव देंगे कि हम इसे फ़ंक्शन संरचना के साथ साफ़ करें। ठीक है, मुझे लगता है। (बीटीडब्लू, मैं हास्केल के साथ एक नौसिखिया हूं, इसलिए शायद मैं इनमें से कुछ के बारे में गलत हूं।)

लेकिन फिर समस्या यह है कि updateMonkeys जैसे कार्य आपको उनके प्रकार के हस्ताक्षर से उपयोगी जानकारी नहीं देते हैं। आप वास्तव में यह सुनिश्चित नहीं कर सकते कि वे क्या करते हैं। निश्चित रूप से, updateMonkeys एक वर्णनात्मक नाम है, लेकिन यह थोड़ा सांत्वना है। जब मैं god object में पास करता हूं और कहता हूं "कृपया मेरी वैश्विक स्थिति अपडेट करें," मुझे लगता है कि हम अनिवार्य दुनिया में वापस आ गए हैं। यह किसी अन्य नाम से वैश्विक चर की तरह लगता है: आपके पास एक ऐसा कार्य है जो वैश्विक स्थिति में कुछ करता है, आप इसे कॉल करते हैं, और आप सबसे अच्छे उम्मीद करते हैं। (मुझे लगता है कि आप अभी भी कुछ समवर्ती समस्याओं से बचें जो एक अनिवार्य कार्यक्रम में वैश्विक चर के साथ मौजूद होंगे। लेकिन मेह, समेकन वैश्विक चर के साथ लगभग एकमात्र चीज गलत नहीं है।)

एक और समस्या यह है: मान लीजिए वस्तुओं को बातचीत करने की जरूरत है। क्योंकि निश्चित ही हम इसे यदि हाथियों के किसी भी किसी भी बंदरों की stomping श्रेणी में हैं देखने के लिए जाँच

stomp :: Elephant -> Monkey -> (Elephant, Monkey) 
stomp elephant monkey = 
    (elongateEvilGrin elephant, decrementHealth monkey) 

इस updateElephants में कहा जाता हो जाता है कहते हैं: उदाहरण के लिए, हम इस तरह एक समारोह है। आप इस परिदृश्य में बंदरों और हाथियों दोनों में परिवर्तनों का सुंदर प्रचार कैसे करते हैं? हमारे दूसरे उदाहरण में, updateElephants लेता है और एक ईश्वर वस्तु देता है, इसलिए यह दोनों परिवर्तनों को प्रभावित कर सकता है। लेकिन यह सिर्फ पानी को आगे बढ़ाता है और मेरे बिंदु को मजबूत करता है: भगवान वस्तु के साथ, आप प्रभावी रूप से वैश्विक चर को बदल रहे हैं। और यदि आप ईश्वर वस्तु का उपयोग नहीं कर रहे हैं, तो मुझे यकीन नहीं है कि आप उन प्रकार के परिवर्तनों का प्रचार कैसे करेंगे।

क्या करना है? निश्चित रूप से कई कार्यक्रमों को जटिल स्थिति का प्रबंधन करने की आवश्यकता है, इसलिए मुझे लगता है कि इस समस्या के कुछ प्रसिद्ध दृष्टिकोण हैं।

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

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

+0

आप कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग – luqui

+4

लिए देख रहे हैं [_Purely कार्यात्मक, घोषणात्मक खेल तर्क रिएक्टिव Programming_ का उपयोग करना] (https://github.com/leonidas/codeblog /blob/master/2012/2012-01-17-declarative-game-logic-afrp.md) आपको सही दिशा में इंगित कर सकता है। –

+0

आप अभी भी बता सकते हैं कि 'अपडेटमॉकीज' क्या करता है, क्योंकि यह ':: राज्य -> ​​बंदर -> राज्य' –

उत्तर

29

उत्तर functional reactive programming (एफआरपी) है। यह दो कोडिंग शैलियों का एक संकर है: घटक राज्य प्रबंधन और समय-निर्भर मूल्य। चूंकि एफआरपी वास्तव में डिजाइन पैटर्न का एक पूरा परिवार है, इसलिए मैं अधिक विशिष्ट होना चाहता हूं: मैं Netwire की अनुशंसा करता हूं।

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

हालांकि यह जटिल और अक्षम लगता है, यह वास्तव में नियमित कार्यों के आसपास बस एक बहुत ही पतली परत है। नेटवायर द्वारा कार्यान्वित डिजाइन पैटर्न एएफआरपी (एरोइज्ड फंक्शनल रिएक्टिव प्रोग्रामिंग) से प्रेरित है। यह शायद अपने नाम (डब्ल्यूएफआरपी?) के लायक होने के लिए काफी अलग है। आप tutorial पढ़ना चाह सकते हैं।

किसी भी मामले में एक छोटा डेमो निम्नानुसार होता है। आपके बिल्डिंग ब्लॉक तार हैं:

myWire :: WireP A B 

इसे एक घटक के रूप में सोचें। यह प्रकार बी की एक समय-अलग मूल्य उस प्रकार एक की एक समय-अलग मूल्य पर, एक सिम्युलेटर में एक कण निर्भर करता है, उदाहरण के लिए है:

particle :: WireP [Particle] Particle 

यह (के लिए कणों की एक सूची पर निर्भर करता है उदाहरण के लिए वर्तमान में सभी मौजूदा कण) और खुद ही एक कण है। के एक पूर्वनिर्धारित तार (एक सरल प्रकार के साथ) का उपयोग करते हैं:

time :: WireP a Time 

इस प्रकार समय (= डबल) एक समय अलग मूल्य है। खैर, यह समय ही है (जब भी तार नेटवर्क शुरू किया गया था तब से 0 से शुरू किया गया)। चूंकि यह किसी अन्य समय पर निर्भर नहीं होता है-अलग-अलग मूल्य जो भी आप चाहते हैं उसे खिला सकते हैं, इसलिए पॉलिमॉर्फिक इनपुट प्रकार। वहाँ भी लगातार तारों (समय अलग-अलग मान कि समय के साथ बदल नहीं है) कर रहे हैं:

pure 15 :: Wire a Integer 

-- or even: 
15 :: Wire a Integer 

दो तारों कनेक्ट करने के लिए आप बस स्पष्ट संरचना का उपयोग करें:

integral_ 3 . 15 

यह आपको 15x पर एक घड़ी देता है वास्तविक समय की गति (समय के साथ 15 से अधिक अभिन्न) 3 (एकीकरण निरंतर) से शुरू होती है। विभिन्न वर्ग के उदाहरणों के लिए धन्यवाद तारों को गठबंधन करने के लिए बहुत आसान हैं। आप अपने नियमित ऑपरेटरों के साथ ही आवेदक शैली या तीर शैली का उपयोग कर सकते हैं। एक घड़ी चाहते हैं जो 10 से शुरू हो और वास्तविक समय की गति से दोगुना हो?

10 + 2*time 

(0 0,) के साथ (0, 0) वेग एक कण है कि शुरू होता है और चाहते हैं और (2, 1) के अनुसार प्रति सेकंड दूसरे के साथ को तेज करता है?

integral_ (0, 0) . integral_ (0, 0) . pure (2, 1) 

आंकड़े दिखाने वाले को उपयोगकर्ता के स्पेस बार प्रेस करना चाहते हैं?

stats . keyDown Spacebar <|> "stats currently disabled" 

यह नेटवायर आपके लिए क्या कर सकता है इसका एक छोटा सा अंश है।

+4

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

1

मुझे पता है कि यह पुराना विषय है। लेकिन मैं व्यायामवाद से रेल बाड़ सिफर अभ्यास को लागू करने की कोशिश करते समय अभी भी एक ही समस्या का सामना कर रहा हूं। हास्केल में इस तरह के खराब ध्यान रखने वाली ऐसी आम समस्या को देखना निराशाजनक है। मैं यह नहीं लेता कि राज्य को बनाए रखने के रूप में कुछ सरल करने के लिए मुझे एफआरपी सीखना होगा। तो, मैं googling जारी रखा और पाया समाधान और अधिक सरल देख - राज्य इकाई: https://en.wikibooks.org/wiki/Haskell/Understanding_monads/State

+0

प्रश्न पहले से ही राज्य मोनड का उल्लेख करता है ("कुछ सुझाव देंगे कि हम एक राज्य मोनाड में गेमस्टेट को लपेटें ...")। समस्या यह है कि एक विशाल वैश्विक स्थिति से कैसे बचें जिस पर कार्यक्रम के प्रत्येक भाग काम कर रहा है। –