functional programming - मैं बस निरंतरता नहीं मिलता!




functional-programming continuations (6)

वे क्या हैं और वे किसके लिए अच्छे हैं?

मेरे पास सीएस की डिग्री नहीं है और मेरी पृष्ठभूमि VB6 है -> एएसपी -> एएसपी.नेट / सी। क्या कोई इसे स्पष्ट और संक्षिप्त तरीके से समझा सकता है?


असल में, एक निरंतरता फ़ंक्शन को निष्पादन को रोकने की क्षमता है और फिर बाद में उस स्थान को वापस उठाता है, जहां बाद में एक बिंदु पर इसे छोड़ दिया गया था। सी # में, आप यह उपज कीवर्ड का उपयोग कर सकते हैं। अगर आप चाहें तो मैं और अधिक विस्तार में जा सकता हूं, लेकिन आप एक संक्षिप्त व्याख्या चाहते थे। ;-)


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

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

(मान लें कि ऑपरेशन एन्क्वे्यू और डेक्यू्यू काम जो कि वैश्विक कतार पर अपेक्षित काम नहीं है जो यहां परिभाषित नहीं है)

(define (fork)
  (display "forking\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue (lambda ()
                (cc #f)))
     (cc #t))))

(define (context-switch)
  (display "context switching\n")
  (call-with-current-continuation
   (lambda (cc)
     (enqueue
      (lambda ()
        (cc 'nothing)))
     ((dequeue)))))

(define (end-process)
  (display "ending process\n")
  (let ((proc (dequeue)))
    (if (eq? proc 'queue-empty)
        (display "all processes terminated\n")
        (proc))))

यह तीन क्रियाओं को प्रदान करता है जो फ़ंक्शन का उपयोग कर सकता है - कांटा, संदर्भ-स्विच और अंत-प्रक्रिया। कांटा संचालन धागा कांटा जाता है और एक उदाहरण # पर और दूसरे में #f लौटाता है संदर्भ-स्विच ऑपरेशन थ्रेड्स के बीच स्विच करता है, और अंत-प्रक्रिया एक थ्रेड को समाप्त करता है

यहां उनका उपयोग का एक उदाहरण है:

(define (test-cs)
  (display "entering test\n")
  (cond
    ((fork) (cond
              ((fork) (display "process 1\n")
                      (context-switch)
                      (display "process 1 again\n"))
              (else (display "process 2\n")
                    (end-process)
                    (display "you shouldn't see this (2)"))))
    (else (cond ((fork) (display "process 3\n")
                        (display "process 3 again\n")
                        (context-switch))
                (else (display "process 4\n")))))
  (context-switch)
  (display "ending process\n")
  (end-process)
  (display "process ended (should only see this once)\n"))

आउटपुट होना चाहिए

entering test
forking
forking
process 1
context switching
forking
process 3
process 3 again
context switching
process 2
ending process
process 1 again
context switching
process 4
context switching
context switching
ending process
ending process
ending process
ending process
ending process
ending process
all processes terminated
process ended (should only see this once)

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

पी एस - मुझे लगता है कि मुझे लिस्प में ऐसा कुछ याद है, इसलिए यदि आप पेशेवर कोड देखना चाहते हैं तो आपको पुस्तक को बाहर की जाँच करनी चाहिए।


मैं अब भी जारी रखने के लिए "उपयोग" कर रहा हूं, लेकिन उनके बारे में सोचने का एक तरीका है कि मुझे प्रोग्राम काउंटर (पीसी) की अवधारणा के निष्कासन के रूप में उपयोगी लगता है स्मृति में निष्पादित करने के लिए अगले निर्देश के लिए एक पीसी "अंक", लेकिन निश्चित रूप से निर्देश (और बहुत ज्यादा हर निर्देश) अंक, निहित या स्पष्ट रूप से, अनुदेश के अनुसार, साथ ही जो भी निर्देशों को सेवा बीच में आना चाहिए। (यहां तक ​​कि एक एनओओपी अनुदेश मेमोरी में अगली शिक्षा के लिए एक कूदता है। लेकिन अगर कोई बाधा उत्पन्न होती है, तो आमतौर पर मेमोरी में कुछ अन्य अनुदेशों के लिए एक जंप शामिल होगा।)

स्मृति में एक प्रोग्राम में प्रत्येक संभावित "लाइव" बिंदु को नियंत्रित करता है, जिसके लिए नियंत्रण किसी भी बिंदु पर कूद सकता है, एक अर्थ में, एक सक्रिय निरंतरता। अन्य बिंदुओं तक पहुंचा जा सकता है जो संभवतया सक्रिय जारी रखे जाते हैं, परन्तु, अधिक से अधिक, वर्तमान में सक्रिय जारी रखने वाले एक या अधिक तक पहुंचने के परिणामस्वरूप संभावित रूप से "गणना" (गतिशील रूप से, संभवतः) वे निरंतरताएं हैं।

यह जारी रखने के लिए परंपरागत परिचयानों में जगह से थोड़ा सा लगता है, जिसमें निष्पादन के सभी लंबित सूत्र स्पष्ट रूप से स्थिर कोड में निरंतरता के रूप में दर्शाए जाते हैं; लेकिन यह इस तथ्य को ध्यान में रखता है कि, सामान्य प्रयोजन वाले कंप्यूटरों पर, पीसी एक अनुदेश अनुक्रम को इंगित करता है जो संभवत: उस अनुदेश अनुक्रम के हिस्से का प्रतिनिधित्व करने वाली स्मृति की सामग्री को बदल सकता है, इस प्रकार अनिवार्य रूप से एक नया (या संशोधित, यदि आप करेंगे ) मक्खी पर निरंतरता, जो वास्तव में सृजन / संशोधन से पहले जारी रखने की सक्रियता के रूप में मौजूद नहीं है।

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

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

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

संक्षेप में, एक निरंतरता "जब लागू किया जाता है तो आगे क्या करना" का एक प्रतिनिधित्व होता है, और, जैसे, (और कुछ भाषाओं में और निरंतरता-पारित-शैली वाले कार्यक्रमों में अक्सर होता है) एक प्रथम-श्रेणी की वस्तु है तत्काल, चारों ओर पारित किया गया, और किसी भी अन्य डेटा प्रकार की तरह ही उतारा गया, और जैसे कि एक क्लासिक कंप्यूटर स्मृति के साथ-साथ पीसी के साथ-साथ व्यवहार करता है - जैसे कि साधारण पूर्णांक के साथ लगभग विनिमेय।


कल्पना कीजिए कि आपके कार्यक्रम में हर एक पंक्ति एक अलग फ़ंक्शन थी। प्रत्येक स्वीकार करता है, एक पैरामीटर के रूप में, निष्पादित करने के लिए अगली पंक्ति / फ़ंक्शन।

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


निरंतरता के बारे में सोचने का एक तरीका प्रोसेसर स्टैक के रूप में है। जब आप "कॉल-टू-चालू-निरंतरता सी" कहते हैं तो यह आपके फ़ंक्शन "c" को कॉल करता है, और "c" को पारित किया जाने वाला पैरामीटर आपके वर्तमान स्टैक पर आपके सभी स्वचालित चर के साथ होता है (इसे एक अन्य फ़ंक्शन के रूप में दर्शाया जाता है, इसे "k ")। इस बीच प्रोसेसर एक नया स्टैक बनाने से शुरू होता है। जब आप "कश्मीर" कहते हैं, तो वह मूल स्टैक पर "सबरॉउटिन से वापसी" (आरटीएस) निर्देश को निष्पादित करता है, और आपको मूल "कॉल-टू-ऑन-ऑन-ऑन-कंटिन्यूशन" ("कॉल-सीसी" के संदर्भ में वापस कूदता है पर) और अपने कार्यक्रम के रूप में पहले जारी रखने की अनुमति। यदि आप "k" के लिए एक पैरामीटर पास करते हैं तो यह "कॉल-सीसी" का रिटर्न मान होता है

अपने मूल स्टैक को देखने के बिंदु से, "कॉल-सीसी" सामान्य फ़ंक्शन कॉल की तरह दिखाई देता है। "सी" के दृष्टिकोण से, आपका मूल स्टैक फ़ंक्शन की तरह दिखता है जो कभी भी रिटर्न नहीं देता

गणितज्ञ के बारे में एक पुरानी मजाक है, जिसने पिंजरे में चढ़कर, ताला लगाकर और पिंजरे के बाहर रहने के लिए एक पिंजरे में एक शेर को पकड़ा था, जबकि बाकी सब कुछ (शेर सहित) उसके अंदर था। निरंतर पिंजरे की तरह थोड़ा सा है, और "सी" गणितज्ञ की तरह थोड़ा सा है। आपका मुख्य कार्यक्रम सोचता है कि "सी" इसके अंदर है, जबकि "सी" का मानना ​​है कि आपका मुख्य प्रोग्राम "के" के अंदर है

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


सी # में, आपके पास दो निरंतरताओं तक पहुंच है। एक, return माध्यम से पहुँचा, एक विधि जारी करने के लिए जहां यह कहा जाता था से जारी है। अन्य, throw माध्यम से पहुंचा, एक विधि निकटतम मिलान catch में जारी रखने देता है।

कुछ भाषाओं ने आपको इन बयानों को प्रथम श्रेणी के मानों के रूप में व्यवहार करने देते हैं, ताकि आप उन्हें असाइन कर सकें और उन्हें वेरिएबल्स में पास कर सकें। इसका क्या मतलब यह है कि आप return या throw के मूल्य को छेड़ सकते हैं और बाद में उन्हें कॉल कर सकते हैं जब आप वास्तव में वापसी या फेंकने के लिए तैयार हों

Continuation callback = return;
callMeLater(callback);

यह कई स्थितियों में आसान हो सकता है एक उदाहरण ऊपर एक जैसा है, जहां आप काम को रोकना चाहते हैं और कुछ देर बाद इसे फिर से शुरू करना चाहते हैं (जैसे कि वेब अनुरोध करना, या कुछ और करना)।

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





callcc