पैटर्न पुनरावृत्ति
पुनरावर्ती पैटर्न के साथ, आपके पास पुनरावर्ती वंश मिलान का एक रूप है ।
यह विभिन्न समस्याओं के लिए ठीक है, लेकिन एक बार जब आप वास्तव में पुनरावर्ती वंश पार्सिंग करना चाहते हैं , तो आपको यहां और वहां कैप्चर समूह सम्मिलित करने की आवश्यकता है, और इस तरह से पूर्ण पार्स संरचना को पुनर्प्राप्त करना अजीब है। डेमियन कॉनवे के रेगेक्सपी :: पर्ल के लिए व्याकरण मॉड्यूल सरल पैटर्न को एक समतुल्य में बदल देता है जो स्वचालित रूप से एक पुनरावर्ती डेटा संरचना में कैप्चर करने वाले सभी का नाम देता है, जो पार्स की गई संरचना की बहुत आसान पुनर्प्राप्ति के लिए बनाता है। मेरे पास इस पोस्ट के अंत में इन दोनों दृष्टिकोणों की तुलना करने वाला एक नमूना है।
रिकर्सन पर प्रतिबंध
सवाल यह था कि पुनरावर्ती पैटर्न किस प्रकार के व्याकरणों से मेल खा सकते हैं। खैर, वे निश्चित रूप से पुनरावर्ती वंश प्रकार मिलान कर रहे हैं । केवल एक चीज जो दिमाग में आती है वह यह है कि पुनरावर्ती पैटर्न बाईं पुनरावृत्ति को संभाल नहीं सकते हैं । यह व्याकरण के प्रकार पर एक बाधा डालता है जिसे आप उन्हें लागू कर सकते हैं। कभी-कभी आप बाईं पुनरावृत्ति को समाप्त करने के लिए अपनी प्रस्तुतियों को पुनः व्यवस्थित कर सकते हैं।
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) | (?"ed_string))
(?<domain> (?&dot_atom) | (?&domain_literal))
(?<domain_literal> (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
\] (?&CFWS)?)
(?<dcontent> (?&dtext) | (?"ed_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) | (?"ed_pair))
(?<quoted_string> (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
(?&FWS)? (?&DQUOTE) (?&CFWS)?)
(?<word> (?&atom) | (?"ed_string))
(?<phrase> (?&word)+)
(?<FWS> (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
(?<ctext> (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
(?<ccontent> (?&ctext) | (?"ed_pair) | (?&comment))
(?<comment> \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
(?<CFWS> (?: (?&FWS)? (?&comment))*
(?: (?:(?&FWS)? (?&comment)) | (?&FWS)))
(?<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 :: व्याकरण मॉड्यूल का उपयोग करके, हम कर सकते हैं।
use strict;
use warnings;
use 5.010;
use Data::Dumper "Dumper";
my $rfc5322 = do {
use Regexp::Grammars;
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>+
<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>)
<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
}
}
जैसा कि आप देखते हैं, पैटर्न में एक बहुत ही अलग संकेतन का उपयोग करके, अब आपको कुछ ऐसा मिलता है जो संपूर्ण पार्स ट्री को %/
चर में आपके लिए दूर रखता है, सब कुछ बड़े करीने से लेबल के साथ। परिवर्तन का परिणाम अभी भी एक पैटर्न है, जैसा कि आप =~
ऑपरेटर द्वारा देख सकते हैं । यह थोड़ा जादुई है।