क्या ढेर संरचना कार्यक्रमों के लिए एकमात्र उचित तरीका है?


74

अधिकांश आर्किटेक्चर मैंने देखा है कि फ़ंक्शन कॉल से पहले संदर्भ को बचाने / पुनर्स्थापित करने के लिए कॉल स्टैक पर निर्भर हैं। यह एक ऐसा सामान्य प्रतिमान है जिसमें पुश और पॉप ऑपरेशन अधिकांश प्रोसेसर में निर्मित होते हैं। क्या ऐसे सिस्टम हैं जो बिना स्टैक के काम करते हैं? यदि हां, तो वे कैसे काम करते हैं, और वे किस लिए उपयोग किए जाते हैं?


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

5
जीएलएसएल स्टैक के बिना काम करता है (जैसा कि उस विशिष्ट ब्रैकेट में अन्य भाषाओं में होता है)। यह बस पुनरावर्ती कॉल को अस्वीकार करता है।
लेउशेंको

3
आप रजिस्टर विंडो में भी देखना चाह सकते हैं , जिनका उपयोग कुछ RISC आर्किटेक्चर द्वारा किया जाता है।
मार्क बूथ

2
@ क्वीन: "अर्ली फोरट्रान कंपाइलर्स ने सबरूटीन्स में किसी तरह की पुनरावृत्ति का समर्थन नहीं किया। शुरुआती कंप्यूटर आर्किटेक्चर ने स्टैक की कोई अवधारणा का समर्थन नहीं किया, और जब उन्होंने सीधे सबरूटीन कॉल का समर्थन किया, तो वापसी स्थान को अक्सर सबरूटीन कोड के निकट एक निश्चित स्थान में संग्रहीत किया जाता था, जो करता है। सबरूटीन की एक पूर्व कॉल वापस आने से पहले एक सबरूटीन को फिर से कॉल करने की अनुमति न दें। हालांकि, फोरट्रान 77 में निर्दिष्ट नहीं है, कई F77 संकलक एक विकल्प के रूप में पुनरावृत्ति का समर्थन करते हैं, जबकि यह फोरट्रान 90 में एक मानक बन गया। " en.wikipedia.org/wiki/Fortran#FORTRAN_II
डक

3
P8X32A ("प्रोपेलर") माइक्रोकंट्रोलर की मानक विधानसभा भाषा (PASM) में स्टैक की कोई अवधारणा नहीं है। कूदने के लिए जिम्मेदार निर्देश भी रैम में रिटर्न निर्देश को स्व-संशोधित करते हैं, यह निर्धारित करने के लिए कि कहां लौटना है - जिसे मनमाने ढंग से चुना जा सकता है। दिलचस्प है, "स्पिन" भाषा (एक उच्च स्तरीय भाषा जो एक ही चिप पर चलती है) में पारंपरिक स्टैक शब्दार्थ है।
वॉसनाम

जवाबों:


50

कॉल स्टैक के लिए एक (कुछ) लोकप्रिय विकल्प निरंतरता है

उदाहरण के लिए, तोता वीएम निरंतरता-आधारित है। यह पूरी तरह से स्टैकलेस है: डेटा को रजिस्टरों में रखा जाता है (जैसे डाल्विक या LuaVM, तोता रजिस्टर आधारित है), और कंट्रोल-फ्लो को निरंतरता (Dalvik या LuaVM के विपरीत, जिसे कॉल स्टैक होता है) के साथ दर्शाया गया है।

एक अन्य लोकप्रिय डेटा संरचना, जिसका उपयोग आमतौर पर स्मॉलटाक और लिस्प वीएम द्वारा किया जाता है, स्पेगेटी स्टैक है, जो कि ढेर के नेटवर्क की तरह है।

जैसा कि @rwong ने बताया , निरंतरता-पासिंग स्टाइल कॉल स्टैक का एक विकल्प है। निरंतरता-गुजर शैली में (या रूपांतरित) में लिखे गए कार्यक्रम कभी वापस नहीं आते हैं, इसलिए स्टैक की कोई आवश्यकता नहीं है।

एक अलग दृष्टिकोण से अपने प्रश्न का उत्तर देना: ढेर पर ढेर फ्रेम आवंटित करके, एक अलग स्टैक के बिना कॉल स्टैक होना संभव है। कुछ लिस्प और स्कीम क्रियान्वयन ऐसा करते हैं।


4
यह आपके स्टैक की परिभाषा पर निर्भर करता है। मुझे यकीन नहीं है कि स्टैक फ़्रेमों की एक लिंक्ड सूची (या पॉइंटर्स की या ...) इतनी अधिक "स्टैक नहीं" "स्टैक का एक अलग प्रतिनिधित्व" है; और सीपीएस भाषाओं के कार्यक्रम (व्यवहार में) निर्माण करने के लिए होते हैं जो निरंतरता से प्रभावी रूप से जुड़ी हुई सूचियाँ हैं जो स्टैक के समान हैं (यदि आप नहीं हैं, तो आप जीएचसी की जांच कर सकते हैं, जो एक रैखिक स्टैक पर "निरंतरता" कहता है। दक्षता के लिए)।
जोनाथन

6
" निरंतरता-गुजर शैली में (या रूपांतरित) में लिखे गए कार्यक्रम कभी वापस नहीं आते हैं " ... अशुभ लगता है।
रॉब पेनरिज

5
@RobPenridge: यह थोड़ा गुप्त है, मैं सहमत हूं। CPS का अर्थ है कि लौटने के बजाय, कार्य एक अतिरिक्त तर्क के रूप में तब लिया जाता है जब उनके कार्य को पूरा करने के लिए कॉल किया जाता है। इसलिए, जब आप किसी फ़ंक्शन को कॉल करते हैं और आपके पास फ़ंक्शन को कॉल करने के बाद आपके पास कुछ और काम करने की आवश्यकता होती है, तो फ़ंक्शन को वापस आने के लिए प्रतीक्षा करने और फिर अपने काम के साथ ले जाने के बजाय, आप शेष कार्य ("निरंतरता") को लपेटते हैं ) एक फ़ंक्शन में, और उस फ़ंक्शन को एक अतिरिक्त तर्क के रूप में पास करें। आपके द्वारा कॉल किया जाने वाला फ़ंक्शन लौटने के बजाय उस फ़ंक्शन को कॉल करता है, और इसी तरह और आगे। कोई समारोह कभी नहीं लौटता है, यह सिर्फ
Jörg W Mittag

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

1
@jcast: एक स्टैक की परिभाषित विशेषता IMO है कि आप केवल सबसे ऊपरी तत्व तक पहुंच सकते हैं। जारी रखने की एक सूची, ओटीओएच, आपको सभी निरंतरताओं तक पहुंच प्रदान करेगा और न केवल सबसे ऊपरी स्टैकफ्रेम। यदि आपके पास Smalltalk- शैली फिर से शुरू करने योग्य अपवाद हैं, उदाहरण के लिए, आपको स्टैक को बिना पॉपअप करने में सक्षम होने की आवश्यकता है। और भाषा में निरंतरता रखते हुए भी एक कॉल स्टैक के परिचित विचार को रखना चाहते हैं, स्पेगेटी स्टैक की ओर जाता है, जो मूल रूप से ढेर का एक पेड़ है जहां स्टैक "जारी" कांटा है।
जोर्ज डब्ल्यू मित्तग

36

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

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

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

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

(हम डेटा संरचनाओं की परिवर्तनशीलता को कार्यों में भी फेंक सकते हैं ...)

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


9
वास्तव में, GPU में अभी भी बिल्कुल भी स्टैक नहीं हैं। आपको GLSL / SPIR-V / OpenCL (एचएसएसएल के बारे में निश्चित नहीं है, लेकिन इसे अलग करने का कोई कारण नहीं दिखता है) में बार-बार लेने से मना किया गया है। जिस तरह से वे वास्तव में फ़ंक्शन कॉल "स्टैक" को संभालते हैं वह एक बड़ी संख्या में रजिस्टरों का उपयोग करके है।
लिनियरजेट्रोपे

@ जेजर: यह एक बड़े पैमाने पर कार्यान्वयन विस्तार है, जैसा कि स्पार्क वास्तुकला से देखा जा सकता है। आपके GPU की तरह, SPARC के पास एक विशाल रजिस्टर सेट है, लेकिन यह इसमें अद्वितीय है कि इसमें एक स्लाइडिंग विंडो है जो रैपपराउंड में बहुत पुरानी रजिस्टरों को रैम में ढेर कर देती है। तो यह वास्तव में दो मॉडलों के बीच एक संकर है। और स्पार्क ने यह निर्दिष्ट नहीं किया कि रजिस्टर विंडो कितनी बड़ी थी, बस कितने भौतिक रजिस्टर थे, इसलिए अलग-अलग कार्यान्वयन कहीं भी "रजिस्टर की भारी मात्रा" के पैमाने पर "बस एक खिड़की के लिए, प्रत्येक फ़ंक्शन कॉल पर" पर्याप्त हो सकते हैं सीधे ढेर करने के लिए "
MSalters

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

14

टी एल; डॉ

  • एक समारोह कॉल तंत्र के रूप में कॉल स्टैक:
    1. आमतौर पर हार्डवेयर द्वारा सिम्युलेटेड है, लेकिन हार्डवेयर के निर्माण के लिए मौलिक नहीं है
    2. अनिवार्य प्रोग्रामिंग के लिए मौलिक है
    3. कार्यात्मक प्रोग्रामिंग के लिए मौलिक नहीं है
  • "लास्ट-इन, फर्स्ट-आउट" (LIFO) के अमूर्त के रूप में स्टैक कंप्यूटर विज्ञान, एल्गोरिदम और यहां तक ​​कि कुछ गैर-तकनीकी डोमेन के लिए मौलिक है।
  • प्रोग्राम संगठन के कुछ उदाहरण जो कॉल स्टैक का उपयोग नहीं करते हैं:
    • कंटीन्यू-पासिंग स्टाइल (CPS)
    • राज्य मशीन - एक विशाल लूप, सब कुछ इनलेटेड। (साब ग्रिपेन फर्मवेयर आर्किटेक्चर से प्रेरित होने के लिए, और हेनरी स्पेंसर द्वारा एक संचार के लिए जिम्मेदार ठहराया गया और जॉन कार्मैक द्वारा पुन: पेश किया गया।) (नोट # 1
    • डाटाफ्लो आर्किटेक्चर - कतार (FIFO) से जुड़े अभिनेताओं का एक नेटवर्क। कतारों को कभी-कभी चैनल भी कहा जाता है।

इस जवाब के बाकी विचारों और उपाख्यानों का एक यादृच्छिक संग्रह है, और इसलिए कुछ हद तक अव्यवस्थित है।


आपके द्वारा वर्णित स्टैक (एक फ़ंक्शन कॉल तंत्र के रूप में) अनिवार्य प्रोग्रामिंग के लिए विशिष्ट है।

अनिवार्य प्रोग्रामिंग के नीचे, आपको मशीन कोड मिलेगा। मशीन कोड निर्देशों के एक छोटे अनुक्रम को निष्पादित करके कॉल स्टैक का अनुकरण कर सकता है।

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

  1. संयुक्त तर्क, अर्थात तर्क द्वार का एक कनेक्शन (और, या, नहीं, ...) ध्यान दें कि "संयोजन तर्क" फीडबैक को छोड़कर।
  2. मेमोरी यानी फ्लिप-फ्लॉप, लैचेस, रजिस्टर, SRAM, DRAM इत्यादि।
  3. एक स्टेट मशीन जिसमें कुछ कॉम्बिनेशन लॉजिक और कुछ मेमोरी होते हैं, बस इतना है कि यह एक "कंट्रोलर" को लागू कर सकता है जो बाकी हार्डवेयर को मैनेज करता है।

निम्नलिखित चर्चाओं में अनिवार्य कार्यक्रमों को संरचित करने के वैकल्पिक तरीकों के उदाहरण शामिल थे।

इस तरह के कार्यक्रम की संरचना इस तरह दिखाई देगी:

void main(void)
{
    do
    {
        // validate inputs for task 1
        // execute task 1, inlined, 
        // must complete in a deterministically short amount of time
        // and limited to a statically allocated amount of memory
        // ...
        // validate inputs for task 2
        // execute task 2, inlined
        // ...
        // validate inputs for task N
        // execute task N, inlined
    }
    while (true);
    // if this line is reached, tell the programmers to prepare
    // themselves to appear before an accident investigation board.
    return 0; 
}

यह शैली माइक्रोकंट्रोलर्स के लिए उपयुक्त होगी, अर्थात जो हार्डवेयर के कार्यों के लिए एक साथी के रूप में सॉफ्टवेयर को देखते हैं।



@Peteris: ढेर LIFO डेटा संरचनाएं हैं।
क्रिस्टोफर Creutzig

1
दिलचस्प। मैंने इसे दूसरे तरीके से सोचा होगा। उदाहरण के लिए, फोरट्रान एक अनिवार्य प्रोग्रामिंग भाषा है, और शुरुआती संस्करणों ने कॉल स्टैक का उपयोग नहीं किया। हालांकि, पुनरावृत्ति कार्यात्मक प्रोग्रामिंग के लिए मौलिक है, और मुझे नहीं लगता कि स्टैक का उपयोग किए बिना सामान्य मामले में लागू करना संभव है।
TED

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

@davidbak - IIRC, एक पुनरावर्ती एल्गोरिथ्म बहुत अधिक पूंछ-पुनरावर्ती होना चाहिए ताकि स्टैक से छुटकारा पाने में सक्षम हो। संभवतः कुछ अन्य मामले हैं जहां आप इसे अनुकूलित कर सकते हैं, लेकिन सामान्य स्थिति में, आपके पास एक स्टैक होना चाहिए । वास्तव में, मुझे बताया गया था कि कहीं न कहीं इसके बारे में एक गणितीय प्रमाण था। यह उत्तर दावा करता है कि यह चर्च-ट्यूरिंग प्रमेय है (मुझे लगता है कि इस तथ्य पर आधारित है कि ट्यूरिंग मशीनें एक स्टैक का उपयोग करती हैं?)
TED

1
@ टेड - मैं आपसे सहमत हूँ। मेरा मानना ​​है कि यहाँ गलतफहमी यह है कि मैंने सिस्टम आर्किटेक्चर के बारे में बात करने के लिए ओपी की पोस्ट पढ़ी, जिसका मतलब मेरे लिए मशीन आर्किटेक्चर था । मुझे लगता है कि जिन अन्य लोगों ने यहां उत्तर दिया है, उनकी समान समझ है। तो हम में से जो समझ गए थे कि संदर्भ होने के लिए जवाब देकर कहा गया है कि आपको मशीन निर्देश / एड्रेसिंग मोड स्तर पर स्टैक की आवश्यकता नहीं है। लेकिन मैं देख सकता हूँ कि प्रश्न का अर्थ यह भी समझा जा सकता है कि, क्या किसी भाषा प्रणाली को सामान्य रूप से कॉल स्टैक की आवश्यकता होती है। वह उत्तर भी नहीं, बल्कि विभिन्न कारणों से है।
दाविदबक

11

नहीं, जरूरी नहीं।

पढ़ें अपेल के पुराने कागज कूड़ा संग्रह ढेर आवंटन की तुलना में तेजी हो सकता है । यह निरंतर गुजर शैली का उपयोग करता है और एक स्टैकलेस कार्यान्वयन दिखाता है।

यह भी देखें कि पुराने कंप्यूटर आर्किटेक्चर (जैसे IBM / 360 ) में कोई हार्डवेयर स्टैक रजिस्टर नहीं था। लेकिन ओएस और कंपाइलर ने कन्वेंशन ( कॉलिंग कन्वेंशन से संबंधित ) द्वारा स्टैक पॉइंटर के लिए एक रजिस्टर आरक्षित किया ताकि वे एक सॉफ्टवेयर कॉल स्टैक हो सकें ।

सिद्धांत रूप में, एक पूरा कार्यक्रम सी कंपाइलर और ऑप्टिमाइज़र मामले का पता लगा सकता है (एम्बेडेड सिस्टम के लिए कुछ सामान्य) जहां कॉल ग्राफ़ को सांख्यिकीय रूप से जाना जाता है और बिना किसी पुनरावर्तन (या फ़ंक्शन पॉइंटर्स) के। इस तरह की प्रणाली में, प्रत्येक फ़ंक्शन एक निश्चित स्थिर स्थान में अपना रिटर्न पता रख सकता है (और इसी तरह से फोर्ट्रान77 ने 1970 के युग के कंप्यूटरों में काम किया था )।

इन दिनों, प्रोसेसर में सीपीयू कैश के बारे में कॉल स्टैक्स (और कॉल एंड रिटर्न मशीन निर्देश) भी होते हैं


1
यकीन है कि FORTRAN स्थिर वापसी स्थानों का उपयोग बंद कर दिया जब FORTRAN -66 बाहर आया और के लिए समर्थन की आवश्यकता SUBROUTINEऔर FUNCTION। आप पहले के संस्करणों के लिए सही हैं, हालांकि (FORTRAN-IV और संभवतः WATFIV)।
टीएमएन

और बेशक,। और आईबीएम / 360 के बारे में उत्कृष्ट बिंदु - लापता हार्डवेयर स्टैक एड्रेसिंग मोड के बावजूद इसका काफी उपयोग हुआ। (R14, मेरा मानना ​​है कि यह था!) ​​और इसमें स्टैक-आधारित भाषाओं के लिए संकलक थे, जैसे, PL / I, Ada, Algol, C.
davidbak

वास्तव में, मैंने कॉलेज में 360 का अध्ययन किया और पाया कि यह पहली बार में हतप्रभ था।
11:30 पर JDługosz

1
@ JDługosz कंप्यूटर वास्तुकला के आधुनिक छात्रों 360 पर विचार करने के लिए सबसे अच्छा तरीका है एक से अधिक अनुदेश प्रारूप ... और जैसे कुछ विसंगतियों के साथ यद्यपि एक बहुत ही सरल RISC मशीन के रूप में है ... TRऔर TRT
दविदबक

रजिस्टर को स्थानांतरित करने के लिए "शून्य और पैक किए गए" के बारे में कैसे? लेकिन रिटर्न एड्रेस के लिए स्टैक के बजाय "ब्रांच और लिंक" ने वापसी की है।
JDługosz

10

आपको अब तक कुछ अच्छे उत्तर मिले हैं; मुझे आप एक अव्यवहारिक लेकिन अत्यधिक शैक्षिक उदाहरण देते हैं कि आप कैसे ढेर या "नियंत्रण प्रवाह" की धारणा के बिना एक भाषा डिजाइन कर सकते हैं। यहां एक ऐसा कार्यक्रम है, जो वास्तविकताओं को निर्धारित करता है:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = f(3)

हम इस प्रोग्राम को एक स्ट्रिंग में रखते हैं, और हम टेक्स्ट के प्रतिस्थापन द्वारा प्रोग्राम का मूल्यांकन करते हैं। इसलिए जब हम मूल्यांकन कर रहे होते हैं f(3), हम एक खोज करते हैं और 3 को i के साथ प्रतिस्थापित करते हैं, जैसे:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = if 3 == 0 then 1 else 3 * f(3 - 1)

महान। अब हम एक और शाब्दिक प्रतिस्थापन करते हैं: हम देखते हैं कि "if" की स्थिति झूठी है और प्रोग्राम को प्रोड्यूस करते हुए एक और स्ट्रिंग को प्रतिस्थापित करते हैं:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = 3 * f(3 - 1)

अब हम स्थिरांक को शामिल करने वाले सभी उप-अभिव्यक्तियों पर एक और स्ट्रिंग करते हैं:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = 3 * f(2)

और तुम देखते हो कि यह कैसे जाता है; मैं आगे इस बिंदु पर श्रम नहीं करूंगा। हम स्ट्रिंग प्रतिस्थापन की एक श्रृंखला को जारी रख सकते हैं जब तक कि हम नीचे नहीं गए let x = 6और हमें किया जाएगा।

हम स्थानीय चर और निरंतरता की जानकारी के लिए पारंपरिक रूप से स्टैक का उपयोग करते हैं; याद रखें, एक स्टैक आपको यह नहीं बताता है कि आप कहां से आए हैं, यह आपको बताता है कि आप हाथ में उस वापसी मूल्य के साथ कहां जा रहे हैं।

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

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


3
रेटिना एक रेगेक्स-आधारित प्रोग्रामिंग भाषा का एक उदाहरण है जो गणना के लिए स्ट्रिंग संचालन का उपयोग करता है।
एंड्रयू पिलिसर

2
@AndrewPiliser इस शांत दोस्त द्वारा डिजाइन और कार्यान्वित किया गया ।
बिल्ली

3

Parnas द्वारा 1972 में प्रकाशन के बाद से मॉड्यूल में सिस्टम को डिकम्पोज करने के लिए उपयोग किए जाने वाले मानदंडों में इसे यथोचित रूप से स्वीकार किया गया है कि सॉफ्टवेयर में जानकारी छिपाना एक अच्छी बात है। यह संरचनात्मक अपघटन और मॉड्यूलर प्रोग्रामिंग पर '60 के दशक में एक लंबी बहस पर चलता है।

प्रतिरूपकता

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

डायनेमिक स्कूपिंग

जैसे ही लेक्सिकल स्कूपिंग गतिशील व्यवहार को ट्रैक करने के लिए अपर्याप्त है, तो अंतर को ट्रैक करने के लिए कुछ रनटाइम बुककीपिंग की आवश्यकता होती है।

किसी भी धागे (परिभाषा के अनुसार) में केवल एक ही वर्तमान अनुदेश सूचक है, एक LIFO- स्टैक प्रत्येक आह्वान को ट्रैक करने के लिए उपयुक्त है।

अपवाद

इसलिए, जबकि निरंतरता मॉडल स्टैक के लिए स्पष्ट रूप से एक डेटा संरचना को बनाए नहीं रखता है, फिर भी मॉड्यूल की नेस्टेड कॉलिंग है जिसे कहीं न कहीं बनाए रखना पड़ता है!

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

रवांग द्वारा पहचाना जाने वाला अंतहीन लूप संरचना उच्च शेड्यूलिंग अनुप्रयोगों में सामान्य है, जो स्थैतिक शेड्यूलिंग के साथ है जो कई सामान्य प्रोग्रामिंग संरचनाओं को बंद कर देता है लेकिन मांग करता है कि पूरे एप्लिकेशन को बिना किसी महत्वपूर्ण जानकारी के सफेद बॉक्स माना जाए।

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


1
आप " किसी भी मल्टी-थ्रेडेड सिस्टम" को मानकर एक कोने में खुद को चित्रित करते हैं । युग्मित परिमित अवस्था मशीनों के कार्यान्वयन में कई धागे हो सकते हैं, लेकिन उन्हें LIFO स्टैक की आवश्यकता नहीं होती है। FSM में कोई सीमा नहीं है कि आप किसी भी पिछले राज्य में लौट आएं, LIFO ऑर्डर में अकेले रहने दें। तो यह एक वास्तविक बहु-थ्रेडेड सिस्टम है जिसके लिए यह होल्ड नहीं करता है। और यदि आप अपने आप को "समानांतर स्वतंत्र फ़ंक्शन कॉल स्टैक" के रूप में बहु-थ्रेडेड की परिभाषा तक सीमित करते हैं, तो आप एक परिपत्र परिभाषा के साथ समाप्त होते हैं।
MSalters

मैं इस तरह से सवाल नहीं पढ़ता हूं। ओपी फ़ंक्शन कॉल से परिचित है, लेकिन अन्य प्रणालियों के बारे में पूछता है ।
MSalters

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

2

सभी पुराने मेनफ्रेम (IBM System / 360) में स्टैक की कोई धारणा नहीं थी। उदाहरण के लिए, 260 पर, स्मृति में एक निश्चित स्थान पर मापदंडों का निर्माण किया गया था और जब एक सबरूटीन कहा जाता था, तो इसे R1पैरामीटर ब्लॉक की ओर इशारा करते हुए और R14वापसी पते से युक्त किया जाता था । रुटीन कहा जाता है, अगर वह किसी अन्य सबरूटीन को कॉल करना चाहता था, तो उसे कॉल करने R14से पहले एक ज्ञात स्थान पर स्टोर करना होगा ।

यह एक स्टैक की तुलना में बहुत अधिक विश्वसनीय है क्योंकि सब कुछ संकलन समय पर स्थापित निश्चित मेमोरी स्थानों में संग्रहीत किया जा सकता है और यह 100% गारंटी हो सकती है कि प्रक्रियाएं कभी भी स्टैक से बाहर नहीं चलेंगी। "Allocate 1MB और अपनी उंगलियों को पार करने" में से कोई भी नहीं है जो हमें आजकल करना है।

कीवर्ड निर्दिष्ट करके PL / I में पुनरावर्ती उप-कॉल की अनुमति दी गई थी RECURSIVE। उनका तात्पर्य था कि उपमहाद्वीप द्वारा उपयोग की जाने वाली स्मृति सांख्यिकीय रूप से आवंटित की बजाय गतिशील रूप से थी। लेकिन पुनरावर्ती कॉल तब दुर्लभ थे जब वे अब हैं।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.