"आधुनिक" रीगेक्स की पहचान शक्ति


83

वास्तविक आधुनिक रीगेक्स को वास्तव में कौन सी भाषाएं पहचानती हैं?

जब भी एक बैक-रेफरेंस (जैसे (.*)_\1) के साथ एक अनबाउंड लंबाई कैप्चरिंग समूह होता है, तो एक रेगेक्स अब एक गैर-नियमित भाषा से मेल खा रहा है। लेकिन यह, अपने दम पर, कुछ S ::= '(' S ')' | ε- कुछ मेल खाने के लिए पर्याप्त नहीं है - प्रसंगों की जोड़ी के संदर्भ-मुक्त भाषा।

रिकर्सिव रेग्जेस (जो मेरे लिए नए हैं, लेकिन मुझे पर्ल और पीसीआरई में मौजूद होने का आश्वासन दिया गया है) कम से कम अधिकांश सीएफएल को पहचानते हैं।

क्या इस क्षेत्र में किसी ने कोई शोध किया है या पढ़ा है? इन "आधुनिक" रेगेक्स की सीमाएं क्या हैं? क्या वे एलएलजी या एलआर व्याकरणों के सीएफजी की तुलना में कड़ाई से अधिक या कड़ाई से कम पहचानते हैं? या क्या ऐसी दोनों भाषाएं मौजूद हैं जिन्हें एक रेगीक्स द्वारा मान्यता दी जा सकती है लेकिन सीएफजी और विपरीत नहीं?

प्रासंगिक कागजात के लिंक की बहुत सराहना की जाएगी।


1
मैं पुनरावर्ती पैटर्न द्वारा हल करने योग्य समस्याओं की संगणनीयता वर्ग में किसी भी औपचारिक काम के बारे में नहीं जानता। मुझे पता है कि आपका पुनरावर्ती उत्पादन पीसीआर या पर्ल में एक पुनरावर्ती पैटर्न के रूप में काफी आसानी से कोडित है।
tchrist

5
क्या यह cstheory.stackexchange.com के लिए बेहतर होगा ?

3
@arcain, मैं वास्तव में इसे "शोध स्तर का प्रश्न" नहीं मानता, क्योंकि इसकी मृत्यु होने की संभावना है ... मैं इसे पोस्ट करने की कोशिश कर सकता हूं अगर मुझे कुछ भी सुनाई नहीं देता ...
tobyodavies

2
@toby - सुनिश्चित करें, लेकिन यह है एक सैद्धांतिक प्रश्न, और cstheory पर समुदाय एक बहुत अधिक विशेष दर्शक हैं। वहाँ मात्रा भी कम है, इसलिए आपके प्रश्न के आसानी से जवाब देने वाले लोगों की बाढ़ में खो जाने की संभावना कम है। मैं सिर्फ आपके सवाल का जवाब देखना चाहता हूं।
आर्क

2
पुरानी पोस्ट, लेकिन मैंने कई बार इस लिंक का उल्लेख किया है: nikic.github.io/2012/06/15/…
Anders

जवाबों:


106

पैटर्न पुनरावृत्ति

पुनरावर्ती पैटर्न के साथ, आपके पास पुनरावर्ती वंश मिलान का एक रूप है ।

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

रिकर्सन पर प्रतिबंध

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

BTW, PCRE और Perl इस बात पर थोड़े अलग हैं कि आपने रिकर्सन को कैसे वाक्यांश दिया जाए। Pcrepattern manpage में "RECATIVE PATTERNS" और "पर्ल से रिकर्सन अंतर" पर अनुभाग देखें । उदाहरण: पर्ल को संभालना ^(.|(.)(?1)\2)$चाहिए जहां PCRE के ^((.)(?1)\2|.)$बजाय आवश्यकता होती है ।

रिकर्सियन डेमो

पुनरावर्ती पैटर्न की आवश्यकता आश्चर्यजनक रूप से अक्सर उत्पन्न होती है। एक अच्छी तरह से देखी गई मिसाल है जब आपको किसी ऐसी चीज़ से मेल खाना चाहिए जो घोंसला कर सके, जैसे कि संतुलित कोष्ठक, उद्धरण या यहां तक ​​कि HTML / XML टैग। यहाँ संतुलित पार्स के लिए मैच है:

\((?:[^()]*+|(?0))*\)

मुझे लगता है कि इसकी कॉम्पैक्ट प्रकृति के कारण पढ़ने में मुश्किल है। यह आसानी से व्हाट्सएप को /xमहत्वपूर्ण बनाने के लिए मोड के साथ प्रयोग करने योग्य है:

\( (?: [^()] *+ | (?0) )* \)

फिर से, चूंकि हम अपनी पुनरावृत्ति के लिए परनों का उपयोग कर रहे हैं, एक स्पष्ट उदाहरण नेस्टेड एकल उद्धरणों का मिलान होगा:

‘ (?: [^‘’] *+ | (?0) )* ’

एक और पुनरावर्ती रूप से परिभाषित चीज जिसे आप मैच करना चाहते हैं वह एक पैलिंड्रोम होगा। यह सरल पैटर्न पर्ल में काम करता है:

^((.)(?1)\2|.?)$

जो आप इस तरह से कुछ का उपयोग कर अधिकांश प्रणालियों पर परीक्षण कर सकते हैं:

$ perl -nle 'print if /^((.)(?1)\2|.?)$/i' /usr/share/dict/words

ध्यान दें कि पीसीआरई के पुनरावर्तन के कार्यान्वयन के लिए अधिक विस्तृत की आवश्यकता होती है

^(?:((.)(?1)\2|)|((.)(?3)\4|.))

इसका कारण यह है कि पीसीआरई की पुनरावृत्ति कैसे काम करती है, इस पर प्रतिबंध है।

उचित पार्सिंग

मेरे लिए, ऊपर के उदाहरण ज्यादातर खिलौना मैचों में बिल्कुल नहीं हैं कि दिलचस्प वास्तव में,। जब यह दिलचस्प हो जाता है जब आपके पास एक वास्तविक व्याकरण होता है जिसे आप पार्स करने की कोशिश कर रहे होते हैं। उदाहरण के लिए, RFC 5322 विस्तृत रूप से मेल पते को परिभाषित करता है। इसे मिलान करने के लिए यहां "व्याकरणिक" पैटर्न है:

$rfc5322 = qr{

   (?(DEFINE)

     (?<address>         (?&mailbox) | (?&group))
     (?<mailbox>         (?&name_addr) | (?&addr_spec))
     (?<name_addr>       (?&display_name)? (?&angle_addr))
     (?<angle_addr>      (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
     (?<group>           (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
     (?<display_name>    (?&phrase))
     (?<mailbox_list>    (?&mailbox) (?: , (?&mailbox))*)

     (?<addr_spec>       (?&local_part) \@ (?&domain))
     (?<local_part>      (?&dot_atom) | (?&quoted_string))
     (?<domain>          (?&dot_atom) | (?&domain_literal))
     (?<domain_literal>  (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                   \] (?&CFWS)?)
     (?<dcontent>        (?&dtext) | (?&quoted_pair))
     (?<dtext>           (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])

     (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
     (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
     (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
     (?<dot_atom_text>   (?&atext)+ (?: \. (?&atext)+)*)

     (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
     (?<quoted_pair>     \\ (?&text))

     (?<qtext>           (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
     (?<qcontent>        (?&qtext) | (?&quoted_pair))
     (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                          (?&FWS)? (?&DQUOTE) (?&CFWS)?)

     (?<word>            (?&atom) | (?&quoted_string))
     (?<phrase>          (?&word)+)

     # Folding white space
     (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
     (?<ctext>           (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
     (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
     (?<comment>         \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
     (?<CFWS>            (?: (?&FWS)? (?&comment))*
                         (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

     # No whitespace control
     (?<NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

     (?<ALPHA>           [A-Za-z])
     (?<DIGIT>           [0-9])
     (?<CRLF>            \x0d \x0a)
     (?<DQUOTE>          ")
     (?<WSP>             [\x20\x09])
   )

   (?&address)

}x;

जैसा कि आप देखते हैं, यह बहुत ही BNF है। समस्या यह है कि यह केवल एक मैच है, एक कैप्चर नहीं है। और आप वास्तव में पूरी तरह से पूरी तरह से कब्जा करने वाले पार्न्स के साथ नहीं चाहते हैं क्योंकि यह आपको नहीं बताता है कि कौन सा उत्पादन किस हिस्से से मेल खाता है। पहले उल्लेख किए गए Regexp :: व्याकरण मॉड्यूल का उपयोग करके, हम कर सकते हैं।

#!/usr/bin/env perl

use strict;
use warnings;
use 5.010;
use Data::Dumper "Dumper";

my $rfc5322 = do {
    use Regexp::Grammars;    # ...the magic is lexically scoped
    qr{

    # Keep the big stick handy, just in case...
    # <debug:on>

    # Match this...
    <address>

    # As defined by these...
    <token: address>         <mailbox> | <group>
    <token: mailbox>         <name_addr> | <addr_spec>
    <token: name_addr>       <display_name>? <angle_addr>
    <token: angle_addr>      <CFWS>? \< <addr_spec> \> <CFWS>?
    <token: group>           <display_name> : (?:<mailbox_list> | <CFWS>)? ; <CFWS>?
    <token: display_name>    <phrase>
    <token: mailbox_list>    <[mailbox]> ** (,)

    <token: addr_spec>       <local_part> \@ <domain>
    <token: local_part>      <dot_atom> | <quoted_string>
    <token: domain>          <dot_atom> | <domain_literal>
    <token: domain_literal>  <CFWS>? \[ (?: <FWS>? <[dcontent]>)* <FWS>?

    <token: dcontent>        <dtext> | <quoted_pair>
    <token: dtext>           <.NO_WS_CTL> | [\x21-\x5a\x5e-\x7e]

    <token: atext>           <.ALPHA> | <.DIGIT> | [!#\$%&'*+-/=?^_`{|}~]
    <token: atom>            <.CFWS>? <.atext>+ <.CFWS>?
    <token: dot_atom>        <.CFWS>? <.dot_atom_text> <.CFWS>?
    <token: dot_atom_text>   <.atext>+ (?: \. <.atext>+)*

    <token: text>            [\x01-\x09\x0b\x0c\x0e-\x7f]
    <token: quoted_pair>     \\ <.text>

    <token: qtext>           <.NO_WS_CTL> | [\x21\x23-\x5b\x5d-\x7e]
    <token: qcontent>        <.qtext> | <.quoted_pair>
    <token: quoted_string>   <.CFWS>? <.DQUOTE> (?:<.FWS>? <.qcontent>)*
                             <.FWS>? <.DQUOTE> <.CFWS>?

    <token: word>            <.atom> | <.quoted_string>
    <token: phrase>          <.word>+

    # Folding white space
    <token: FWS>             (?: <.WSP>* <.CRLF>)? <.WSP>+
    <token: ctext>           <.NO_WS_CTL> | [\x21-\x27\x2a-\x5b\x5d-\x7e]
    <token: ccontent>        <.ctext> | <.quoted_pair> | <.comment>
    <token: comment>         \( (?: <.FWS>? <.ccontent>)* <.FWS>? \)
    <token: CFWS>            (?: <.FWS>? <.comment>)*
                             (?: (?:<.FWS>? <.comment>) | <.FWS>)

    # No whitespace control
    <token: NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f]
    <token: ALPHA>           [A-Za-z]
    <token: DIGIT>           [0-9]
    <token: CRLF>            \x0d \x0a
    <token: DQUOTE>          "
    <token: WSP>             [\x20\x09]
    }x;
};

while (my $input = <>) {
    if ($input =~ $rfc5322) {
        say Dumper \%/;       # ...the parse tree of any successful match
                              # appears in this punctuation variable
    }
}

जैसा कि आप देखते हैं, पैटर्न में एक बहुत ही अलग संकेतन का उपयोग करके, अब आपको कुछ ऐसा मिलता है जो संपूर्ण पार्स ट्री को %/चर में आपके लिए दूर रखता है, सब कुछ बड़े करीने से लेबल के साथ। परिवर्तन का परिणाम अभी भी एक पैटर्न है, जैसा कि आप =~ऑपरेटर द्वारा देख सकते हैं । यह थोड़ा जादुई है।


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

7
@tobyodavies: मैं पीसीआरई प्रतिबंधों को आगे समझा सकता था; उन्हें समूहों की परमाणुता के साथ करना होगा: आप ऐसे समूह पर पुनरावृत्ति नहीं कर सकते हैं जो अभी तक PCRE में पूरा नहीं हुआ है, लेकिन आप पर्ल में कर सकते हैं। पीसीआर में व्याकरणिक आरएफसी 5322 पैटर्न समान रूप से अच्छी तरह से काम करना चाहिए; संपूर्ण ((DEFINE)…)विचार अत्यंत शक्तिशाली और उपयोगी है, सभी शीर्ष-डाउन प्रोग्रामिंग की तरह, निष्पादन से घोषणा (और इसके आदेश) को अलग करने की अनुमति देता है। मुझे याद नहीं आ रहा है कि किन अन्य भाषाओं में ग्रुप रिकर्सन है; यह C♯ या इसके ilk की तरह कुछ विदेशी हो सकता है।
tchrist
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.