आम आदमी की शर्तों में, क्या बचा है?


12

Code.google.com पर एक पृष्ठ के अनुसार , "बाईं पुनरावृत्ति" को निम्नानुसार परिभाषित किया गया है:

लेफ्ट रिकर्सन केवल किसी भी पुनरावर्ती नॉनटर्मिनल को संदर्भित करता है, जब यह एक संवेदी रूप से युक्त होता है, जो उत्पादन नियम के बाईं ओर स्वयं की नई प्रति दिखाई देता है।

विकिपीडिया दो भिन्न परिभाषाएँ प्रस्तुत करता है:

  1. संदर्भ-मुक्त व्याकरण के संदर्भ में, एक गैर-टर्मिनल आर बाईं-पुनरावर्ती है यदि किसी भी आर प्रोडक्शंस ('विकल्प') में से किसी एक में बाएं-सबसे प्रतीक तुरंत (प्रत्यक्ष / तत्काल बाएं-पुनरावर्ती) या किसी अन्य गैर-टर्मिनल के माध्यम से परिभाषाएँ (अप्रत्यक्ष / छुपी हुई-पुनरावर्ती) पुन: लिखने के लिए पुन: लिखती हैं।

  2. "एक व्याकरण वाम-पुनरावर्ती है यदि हम कुछ गैर-टर्मिनल ए पा सकते हैं जो अंततः बाएं प्रतीक के रूप में स्वयं के साथ एक भावुक रूप प्राप्त करेगा।"

मैं यहां भाषा निर्माण के साथ मुश्किल से शुरुआत कर रहा हूं, और अपने खाली समय में कर रहा हूं। हालाँकि जब यह एक भाषा पार्सर का चयन करने के लिए आता है, तो क्या बाईं पुनरावृत्ति इस पार्सर द्वारा समर्थित है या यह कि पार्सर एक मुद्दा है जो तुरंत सामने और केंद्र में आता है। "भावुक रूप" जैसे शब्दों को देखने से केवल शब्दजाल की आगे की सूचियों की ओर जाता है, लेकिन "वाम" पुनरावृत्ति का भेद कुछ बहुत ही सरल होना चाहिए। अनुवाद कृपया?

जवाबों:


21

एक नियम Rबाएं-पुनरावर्ती है यदि, यह पता लगाने के लिए कि क्या Rमैच होता है, आपको पहले पता लगाना होगा कि क्या Rमैच। ऐसा तब Rहोता है, जब प्रत्यक्ष या अप्रत्यक्ष रूप से, स्वयं के कुछ उत्पादन में पहला शब्द होता है।

गणितीय भावों के व्याकरण के एक खिलौना संस्करण की कल्पना करें, व्याकुलता से बचने के लिए केवल जोड़ और गुणा के साथ:

Expression ::= Multiplication '+' Expression
            || Multiplication

Multiplication ::= Term '*' Term
                 || Term

Term ::= Number | Variable

जैसा कि लिखा गया है, यहाँ कोई ले-रिकर्सियन नहीं है - हम इस व्याकरण को पुनरावर्ती-वंशीय पार्सर के पास भेज सकते हैं।

लेकिन मान लीजिए कि आपने इसे इस तरह से लिखने की कोशिश की:

Expression ::= Expression '*' Expression
            || Expression '+' Expression
            || Term

Term ::= Number | Variable

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

इससे कोई फर्क नहीं पड़ता कि नियम प्रत्यक्ष या परोक्ष रूप से खुद को संदर्भित करता है; अगर Aउस के साथ शुरू होता है एक विकल्प है B, और Bएक वैकल्पिक है के साथ शुरू होता Aहै, तो Aऔर Bदोनों परोक्ष रूप से छोड़ दिया पुनरावर्ती, और उनके मिलान कार्यों पार्सर एक रिकर्सिव-डिसेंट में अंतहीन आपसी प्रत्यावर्तन के लिए नेतृत्व करेंगे।


तो दूसरे उदाहरण में, यदि आप के बाद बहुत पहली बात बदल ::=से Expressionकरने के लिए Term, और यदि आप पहले के बाद भी ऐसा ही किया ||, वह अब बाएं पुनरावर्ती किया जाएगा? लेकिन अगर आपने इसे केवल बाद में ही किया है ::=, लेकिन ऐसा नहीं है ||, यह अभी भी बचा हुआ है?
पैन्ज़रक्रिसिस

ऐसा लगता है कि आप कह रहे हैं कि बहुत सारे पार्सर बाएं से दाएं जाते हैं, हर प्रतीक पर रुकते हैं और मौके पर पुनरावृत्ति करते हैं। इस मामले में, अगर पहले और बाद में दोनों के Expressionसाथ स्विच किया जाना था, तो सब कुछ ठीक होगा; क्योंकि जितनी जल्दी या बाद में, यह ऐसी चीज़ में चला जाएगा जो न तो एक है और न ही इस तरह, यह निर्धारित करने में सक्षम है कि कुछ और आगे निष्पादन के बिना नहीं है ...Term::=||NumberVariableExpression
Panzercrisis

... लेकिन अगर उनमें से एक अभी भी साथ शुरू हुआ है Expression, तो यह संभावित रूप से कुछ ऐसा होगा जो एक नहीं है Term, और यह सब कुछ Expressionऔर अधिक होने पर बस जाँच करता रहेगा । यही है क्या?
पैन्ज़रक्रिसिस

1
@Panzercrisis कमोबेश। आपको वास्तव में LL, LR, और पुनरावर्ती-वंशीय पार्सर के अर्थ देखने की आवश्यकता है।
हॉब्स

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

4

मैं इसे आम आदमी की शर्तों में डालकर एक छुरा लूंगा।

यदि आप पार्स ट्री (एएसटी नहीं, बल्कि पार्सर की यात्रा और इनपुट के विस्तार) के संदर्भ में सोचते हैं, तो एक पेड़ में पुनरावृत्ति का परिणाम होता है जो बाएं और नीचे बढ़ता है। सही पुनरावृत्ति इसके विपरीत है।

एक उदाहरण के रूप में, एक संकलक में एक सामान्य व्याकरण मदों की एक सूची है। आओ हम स्ट्रिंग्स की सूची लें ("लाल", "ग्रीन", "ब्लू") और इसे पार्स करें। मैं व्याकरण को कुछ तरीके लिख सकता था। निम्नलिखित उदाहरण क्रमशः बाएँ या दाएँ पुनरावर्ती हैं, क्रमशः:

arg_list:                           arg_list:
      STRING                              STRING
    | arg_list ',' STRING               | STRING ',' arg_list 

इन पार्स के लिए पेड़:

         (arg_list)                       (arg_list)
          /      \                         /      \
      (arg_list)  BLUE                  RED     (arg_list)
       /       \                                 /      \
   (arg_list) GREEN                          GREEN    (arg_list)
    /                                                  /
 RED                                                BLUE

ध्यान दें कि यह पुनरावर्तन की दिशा में कैसे बढ़ता है।

यह वास्तव में कोई समस्या नहीं है, यह एक बाईं पुनरावर्ती व्याकरण लिखना चाहता है ठीक है ... यदि आपका पार्सर उपकरण इसे संभाल सकता है। नीचे वाले पार्सर्स इसे ठीक से संभालते हैं। तो आधुनिक एलएल पार्सर कर सकते हैं। पुनरावर्ती व्याकरणों के साथ समस्या पुनरावृत्ति नहीं है, यह पार्सर को आगे बढ़ाए बिना या एक टोकन का उपभोग किए बिना पुनरावृत्ति है। यदि हम पुनरावृत्ति होने पर हमेशा कम से कम 1 टोकन का उपभोग करते हैं, तो हम अंततः पार्स के अंत तक पहुंचते हैं। लेफ्ट रिकर्सन को बिना खपत के रिकेशिंग के रूप में परिभाषित किया गया है, जो एक अनंत लूप है।

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

ऊपर दिए गए व्याकरण के लिए, लेफ्ट रिकर्शन प्रदर्शित करने वाला एलएल कार्यान्वयन इस तरह दिखेगा। पार्सर एक सूची की भविष्यवाणी के साथ शुरू होगा ...

bool match_list()
{
    if(lookahead-predicts-something-besides-comma) {
       match_STRING();
    } else if(lookahead-is-comma) {
       match_list();   // left-recursion, infinite loop/stack overflow
       match(',');
       match_STRING();
    } else {
       throw new ParseException();
    }
}

वास्तव में, जो हम वास्तव में कर रहे हैं वह है "भोली कार्यान्वयन", यानी। हमने शुरू में एक दिए गए वाक्य की भविष्यवाणी की, फिर पुनरावृत्ति को उस भविष्यवाणी के लिए फ़ंक्शन कहा जाता है, और वह फ़ंक्शन भोलेपन से उसी भविष्यवाणी को फिर से कहता है।

नीचे के पार्सरों के पास किसी भी दिशा में पुनरावर्ती नियमों की समस्या नहीं है, क्योंकि वे एक वाक्य की शुरुआत को वापस नहीं करते हैं, वे वाक्य को एक साथ रखकर काम करते हैं।

एक व्याकरण में पुनरावृत्ति केवल एक समस्या है अगर हम ऊपर से नीचे का उत्पादन करते हैं, अर्थात। जब हम टोकन का उपभोग करते हैं तो हमारा पार्सर हमारी भविष्यवाणियों को "विस्तारित" करके काम करता है। यदि विस्तार करने के बजाय, हम गिर जाते हैं (निर्माण "कम हो जाते हैं"), जैसा कि एक एलएएलआर (Yacc / बाइसन) में पार्सर के नीचे होता है, तो दोनों तरफ की पुनरावृत्ति समस्या नहीं है।

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