क्या नियमित अभिव्यक्ति सिंटैक्स डिज़ाइन की खराब पठनीयता का एक विशिष्ट कारण है?


160

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

हम सभी सहमत हैं कि इससे selfDocumentingMethodName()कहीं बेहतर है e()। नियमित भावों पर भी क्यों लागू नहीं होना चाहिए?

यह मुझे लगता है कि एक संरचनात्मक संगठन के साथ एक-लाइन तर्क के वाक्य रचना को डिजाइन करने के बजाय:

var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

और यह एक यूआरएल की भी सख्त पार्स नहीं है!

इसके बजाय, हम एक बुनियादी उदाहरण के लिए संगठित और पठनीय कुछ पाइपलाइन संरचना बना सकते हैं:

string.regex
   .isRange('A-Z' || 'a-z')
   .followedBy('/r');

एक नियमित अभिव्यक्ति के अत्यंत संक्षिप्त वाक्यविन्यास से क्या फायदा होता है जो कम से कम संभव संचालन और तर्क वाक्य रचना के अलावा प्रदान करता है? अंततः, क्या नियमित अभिव्यक्ति सिंटैक्स डिज़ाइन की खराब पठनीयता का एक विशिष्ट तकनीकी कारण है?


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
maple_shaft

1
मैंने RegexToolbox नामक लाइब्रेरी के साथ इस पठनीयता की समस्या से निपटने की कोशिश की है। अब तक इसे C #, Java और JavaScript में पोर्ट किया गया है - github.com/markwhitaker/RegexToolbox.CSharp देखें ।
मार्क व्हाइटेकर

इस मुद्दे को हल करने के लिए कई प्रयास किए गए हैं, लेकिन संस्कृति को बदलना मुश्किल है। यहाँ मौखिक अभिव्यक्तियों के बारे में मेरा उत्तर देखें । सबसे कम उपलब्ध सामान्य टूल के लिए लोग पहुंचते हैं।
परिवार सराफ

जवाबों:


178

एक बड़ा कारण है कि नियमित अभिव्यक्तियों को उनके जैसे शब्द के रूप में डिज़ाइन किया गया था: उन्हें कोड संपादक के लिए कमांड के रूप में उपयोग करने के लिए डिज़ाइन किया गया था, न कि कोड करने के लिए एक भाषा के रूप में। अधिक सटीक रूप से, edनियमित अभिव्यक्ति का उपयोग करने वाले पहले कार्यक्रमों में से एक था । , और वहाँ से नियमित अभिव्यक्तियों ने विश्व प्रभुत्व के लिए अपनी विजय शुरू की। उदाहरण के लिए, edकमांड ने g/<regular expression>/pजल्द ही एक अलग कार्यक्रम को प्रेरित किया grep, जिसे आज भी उपयोग किया जाता है। अपनी शक्ति के कारण, वे बाद में मानकीकृत किए गए और विभिन्न उपकरणों जैसे sedऔर में उपयोग किए गएvim

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


5
रेगेक्स का मतलब पार्स करना नहीं है। अन्यथा, stackoverflow.com/questions/1732348/… । और सिरदर्द।
njzk2

19
@ njzk2 यह उत्तर वास्तव में गलत है। एक HTML दस्तावेज़ एक नियमित भाषा नहीं है, लेकिन एक HTML खुला टैग है , जो कि सवाल के बारे में पूछता है, वास्तव में है।
रैंडम 832

11
यह एक अच्छा जवाब है कि मूल रेगेक्स जितना क्रिप्टोकरंसी क्यों है, यह समझाने के लिए एक अच्छा जवाब है, लेकिन यह नहीं बताता है कि वर्तमान में बढ़ी हुई पठनीयता के साथ कोई वैकल्पिक मानक क्यों नहीं है।
डॉक्टर ब्राउन

13
तो उन सोच के लिए जो grepएक गलत "हड़पने" है, यह वास्तव में g/ re(नियमित अभिव्यक्ति के लिए) / से आता है p?
हेगन वॉन एतेजेन सेप

6
@DannyPflughoeft नहीं, यह नहीं है। एक खुला टैग बस है <aaa bbb="ccc" ddd='eee'>, इसके अंदर नेस्टेड कोई टैग नहीं हैं। आप टैग्स को घोंसला नहीं बना सकते हैं, आप जो घोंसले हैं, वे तत्व हैं (खुले टैग, बाल तत्वों सहित सामग्री, करीबी टैग), जो प्रश्न पार्स करने के बारे में नहीं पूछ रहा था । HTML टैग एक नियमित भाषा है - टैगिंग के ऊपर स्तर पर संतुलन / घोंसला होता है।
रैंडम 832

62

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

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

इसके बावजूद, नियमित अभिव्यक्तियों का अक्सर उपयोग किया जाता है, अर्थात, तब भी लागू किया जाता है जब कोई अन्य उपकरण बहुत बेहतर होगा। मुझे नहीं लगता कि रेगेक्स सिंटैक्स भयानक है । लेकिन यह छोटे और सरल पैटर्न में स्पष्ट रूप से बहुत बेहतर है: सी-जैसी भाषाओं में पहचानकर्ताओं का कट्टरपंथी उदाहरण, [a-zA-Z_][a-zA-Z0-9_]*रेगेक्स ज्ञान का एक न्यूनतम न्यूनतम के साथ पढ़ा जा सकता है और एक बार जब यह मिलता है तो यह स्पष्ट और अच्छी तरह से रसीला दोनों है। कम पात्रों की आवश्यकता स्वाभाविक रूप से खराब नहीं है, काफी विपरीत है। संक्षिप्त होना एक ऐसा गुण है बशर्ते आप समझदार बने रहें।

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

तो वे अधिक जटिल मामलों में कम क्यों पड़ते हैं? मैं तीन मुख्य समस्याएं देख सकता हूं:

  1. कोई अमूर्त क्षमता नहीं हैं। औपचारिक व्याकरण, जो सैद्धांतिक कंप्यूटर विज्ञान के एक ही क्षेत्र से उत्पन्न होते हैं, जो कि एक सेट है, जिसमें प्रस्तुतियों का एक सेट है, इसलिए वे पैटर्न के मध्यवर्ती भागों को नाम दे सकते हैं:

    # This is not equivalent to the regex in the question
    # It's just a mock-up of what a grammar could look like
    url      ::= protocol? '/'? '/'? '/'? (domain_part '.')+ tld
    protocol ::= letter+ ':'
    ...
    
  2. जैसा कि हम ऊपर देख सकते हैं, व्हॉट्सएप का कोई विशेष महत्व नहीं है, प्रारूपण की अनुमति देने के लिए उपयोगी है जो आंखों पर आसान है। टिप्पणियों के साथ एक ही बात। नियमित अभिव्यक्ति ऐसा नहीं कर सकती क्योंकि एक जगह बस एक शाब्दिक है ' '। हालांकि ध्यान दें: कुछ कार्यान्वयन एक "क्रिया" मोड की अनुमति देते हैं जहां व्हाट्सएप को अनदेखा किया जाता है और टिप्पणियां संभव हैं।

  3. सामान्य पैटर्न और कॉम्बिनेटर का वर्णन करने के लिए कोई मेटा-भाषा नहीं है। उदाहरण के लिए, कोई व्यक्ति एक digitबार एक नियम लिख सकता है और इसे एक संदर्भ मुक्त व्याकरण में उपयोग कर सकता है, लेकिन कोई एक "फ़ंक्शन" को परिभाषित नहीं कर सकता है ताकि यह कहा जा सके कि एक उत्पादन दिया गया है pऔर एक नया उत्पादन बनाता है जो इसके साथ कुछ अतिरिक्त करता है, उदाहरण के लिए बनाएँ अल्पविराम से अलग होने की सूची के लिए एक उत्पादन p

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

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

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


9
I don't know enough about the early years of computing to explain how regular expressions came to be so popular.हम हालांकि एक अनुमान को खतरे में डाल सकते हैं: एक बुनियादी नियमित अभिव्यक्ति इंजन को लागू करना बहुत आसान है, एक कुशल संदर्भ-मुक्त पार्सर की तुलना में बहुत आसान है।
19

15
@biziclop मैं इस चर को नजरअंदाज नहीं करेगा। Yacc, जो स्पष्ट रूप से पर्याप्त पूर्ववर्तियों को " अभी तक एक और संकलक कंपाइलर" कहा जाता था, 70 के दशक की शुरुआत में बनाया गया था, और यूनिक्स को एक संस्करण में शामिल किया grepगया था (संस्करण 3 बनाम संस्करण 4)। ऐसा प्रतीत होता है कि रेगेक्स का पहला बड़ा उपयोग 1968 में हुआ था।

मैं केवल वही पा सकता हूं जो मैंने विकिपीडिया पर पाया था (इसलिए मैं इसे 100% विश्वास नहीं करूंगा) लेकिन उसी के अनुसार, yacc1975 में बनाया गया था, LALR पार्सर्स का पूरा विचार (जो व्यावहारिक रूप से प्रयोग करने योग्य पार्सर्स के प्रथम श्रेणी में शामिल थे) १ ९ )३ में उत्पन्न हुआ। जहाँ पहला regexp इंजन कार्यान्वयन था, जिसे JIT ने संकलित किया था (!) १ ९ ६ 1973 में प्रकाशित हुआ था। लेकिन आप सही कह रहे हैं, यह कहना मुश्किल है कि इसे किस तरह से लाया गया, वास्तव में यह कहना मुश्किल है कि जब regexes शुरू हुआ "लेना" बंद "। लेकिन मुझे संदेह है कि एक बार जब वे टेक्स्ट एडिटर्स डेवलपर्स का इस्तेमाल करते थे, तो वे उन्हें अपने सॉफ़्टवेयर में भी इस्तेमाल करना चाहते थे।
बाइज़िकलोप

1
@ jpmc26 ने अपनी पुस्तक, जावास्क्रिप्ट द गुड पार्ट्स टू द रेगेक्स चैप्टर खोला ।
विजिओनरी

2
with very few differences between dialectsमैं यह नहीं कहूंगा कि यह "बहुत कम" है। किसी भी पूर्वनिर्धारित चरित्र वर्ग की विभिन्न बोलियों के बीच कई परिभाषाएँ हैं। और प्रत्येक बोली के लिए विशिष्ट क्वर्की भी हैं।
नपहठ

39

एेतिहाँसिक विचाराे से

विकिपीडिया लेख नियमित अभिव्यक्तियों की उत्पत्ति के बारे में काफी विस्तृत है (क्लेन, 1956)। मूल वाक्य रचना के साथ ही अपेक्षाकृत सरल था *, +, ?, |और समूहीकरण (...)। यह ट्रिब्यूट था ( और पठनीय, दोनों का विरोध जरूरी नहीं है), क्योंकि औपचारिक भाषाएं ट्रिट गणितीय अंकन के साथ व्यक्त की जाती हैं।

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

स्ट्रिंग-आधारित नियमित अभिव्यक्तियों से परे

वैकल्पिक वाक्यविन्यास के बारे में बोलते हुए, आइए एक नज़र डालते हैं जो पहले से मौजूद है ( सीएल-पीपीसीआर , कॉमन लिस्प में )। आपकी लंबी नियमित अभिव्यक्ति ppcre:parse-stringको निम्न प्रकार से पार्स किया जा सकता है:

(let ((*print-case* :downcase)
      (*print-right-margin* 50))
  (pprint
   (ppcre:parse-string "^(?:([A-Za-z]+):)?(\\/{0,3})(0-9.\\-A-Za-z]+)(?::(\\d+))?(?:\\/([^?#]*))?(?:\\?([^#]*))?(?:#(.*))?$")))

... और निम्नलिखित रूप में परिणाम:

(:sequence :start-anchor
 (:greedy-repetition 0 1
  (:group
   (:sequence
    (:register
     (:greedy-repetition 1 nil
      (:char-class (:range #\A #\Z)
       (:range #\a #\z))))
    #\:)))
 (:register (:greedy-repetition 0 3 #\/))
 (:register
  (:sequence "0-9" :everything "-A-Za-z"
   (:greedy-repetition 1 nil #\])))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\:
    (:register
     (:greedy-repetition 1 nil :digit-class)))))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\/
    (:register
     (:greedy-repetition 0 nil
      (:inverted-char-class #\? #\#))))))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\?
    (:register
     (:greedy-repetition 0 nil
      (:inverted-char-class #\#))))))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\#
    (:register
     (:greedy-repetition 0 nil :everything)))))
 :end-anchor)

यह वाक्यविन्यास अधिक क्रियात्मक है, और यदि आप नीचे टिप्पणियों को देखते हैं, तो जरूरी नहीं कि अधिक पठनीय हो। इसलिए यह मत समझिए कि क्योंकि आपके पास कम कॉम्पैक्ट सिंटैक्स है, इसलिए चीजें अपने आप साफ हो जाएंगी

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

`(:sequence
   :start-anchor
   ,(protocol)
   ,(slashes)
   ,(domain)
   ,(top-level-domain) ... )

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

यह भी ध्यान दें कि उपरोक्त प्रपत्र (:regex "string")रूपों की अनुमति देता है ताकि आप पेड़ों के साथ उलटी धारणाओं को मिला सकें। यह सब IMHO को अच्छी पठनीयता और रचनाशीलता की ओर ले जाता है; यह अप्रत्यक्ष रूप से (अर्थात, नियमित अभिव्यक्ति की भाषा में नहीं) द्वारा व्यक्त की गई तीन समस्याओं को संबोधित करता है

समाप्त करने के लिए

  • अधिकांश प्रयोजन के लिए, कविता का अंकन वास्तव में पठनीय है। विस्तारित नोटेशन से निपटने में कठिनाइयाँ होती हैं, जिसमें पीछे हटना आदि शामिल हैं, लेकिन उनका उपयोग शायद ही कभी उचित होता है। नियमित अभिव्यक्ति के अनुचित उपयोग से अपठनीय अभिव्यक्ति हो सकती है।

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

  • वैकल्पिक रूप से, औपचारिक व्याकरण अधिक पठनीय होते हैं और नामकरण और अमूर्त उप-अभिव्यक्तियों में बेहतर होते हैं। टर्मिनलों को आम तौर पर सरल नियमित अभिव्यक्तियों के रूप में व्यक्त किया जाता है।


1. आप पठन-पाठन में अपने भावों का निर्माण करना पसंद कर सकते हैं, क्योंकि नियमित अभिव्यक्तियाँ किसी अनुप्रयोग में स्थिर होती हैं। देखें create-scannerऔर load-time-value:

'(:sequence :start-anchor #.(protocol) #.(slashes) ... )

5
शायद मैं सिर्फ पारंपरिक RegEx वाक्यविन्यास करने के लिए उपयोग किया जाता हूं, लेकिन मुझे यकीन नहीं है कि 22 कुछ पठनीय लाइनें समतुल्य एक पंक्ति regex की तुलना में समझने में आसान हैं।

3
@ dan1111 "कुछ हद तक पठनीय" ;-) ठीक है, लेकिन अगर आप एक बहुत लंबा regex की आवश्यकता है, यह भावना सबसेट, की तरह परिभाषित करने के लिए बनाता है digits, identहै, और उन्हें लिखें। वे जिस तरह से मुझे देखते हैं वह आम तौर पर स्ट्रिंग जोड़तोड़ (संघनन या प्रक्षेप) के साथ होता है, जो उचित पलायन जैसी अन्य समस्याएं लाता है। \\\\`उदाहरण के लिए, emacs संकुल में होने वाली घटनाओं के लिए खोजें । Btw, यह बदतर बना दिया जाता है क्योंकि एक ही एस्केप चरित्र का उपयोग विशेष वर्णों के लिए \nऔर जैसे \"रेगेक्स सिंटैक्स के लिए किया जाता है \(। अच्छे सिंटैक्स का एक गैर-लिस्प उदाहरण है printf, जहां %dसंघर्ष नहीं होता है \d
coredump

1
परिभाषित सबसेट के बारे में उचित बिंदु। यह काफी काम की बात है। मुझे सिर्फ संदेह है कि क्रिया में सुधार है। यह शुरुआती लोगों के लिए आसान हो सकता है (हालांकि जैसी अवधारणाएं greedy-repetitionसहज नहीं हैं और अभी भी सीखना है)। हालांकि, यह विशेषज्ञों के लिए प्रयोज्यता का बलिदान करता है, क्योंकि यह पूरे पैटर्न को देखने और पकड़ना बहुत कठिन है।

@ dan1111 मैं इस बात से सहमत हूं कि अपने आप से क्रियाशीलता में सुधार नहीं है। एक सुधार क्या हो सकता है स्ट्रिंग्स के बजाय संरचित डेटा का उपयोग करके रेगेक्स में हेरफेर कर रहा है।
coredump

@ dan1111 शायद मुझे हास्केल का उपयोग करके एक संपादन का प्रस्ताव देना चाहिए? पारसेक इसे केवल नौ लाइनों में करता है; एक-लाइनर के रूप में do {optional (many1 (letter) >> char ':'); choice (map string ["///","//","/",""]); many1 (oneOf "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."); optional (char ':' >> many1 digit); optional (char '/' >> many (noneOf "?#")); optional (char '?' >> many (noneOf "#")); optional (char '#' >> many (noneOf "\n")); eof}:। कुछ पंक्तियों के साथ जैसे लंबी स्ट्रिंग को डिजाइन करना domainChars = ...और section start p = optional (char start >> many p)यह बहुत सरल लगता है।
सीआर ड्रॉस्ट

25

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

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

उदाहरण के लिए rfc3986 में URI सिंटैक्स देखें :

URI           = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
scheme        = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
hier-part     = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty
...

आप रेगेक्स सिंटैक्स के एक प्रकार का उपयोग करके लगभग एक ही बात लिख सकते हैं जो उप-अभिव्यक्तियों नाम के एम्बेडिंग का समर्थन करता है।


व्यक्तिगत रूप से मुझे लगता है कि आमतौर पर इस्तेमाल की जाने वाली विशेषताओं जैसे चरित्र-वर्ग, संयोजन, विकल्प या पुनरावृत्ति के लिए सिंटैक्स की तरह एक ट्रिक रेगेक्स ठीक है, लेकिन अधिक जटिल और दुर्लभ विशेषताओं के लिए जैसे लुक-फॉर-वर्बोज़ नाम बेहतर हैं। काफी हद तक हम सामान्य प्रोग्रामिंग में +या जैसे ऑपरेटरों का उपयोग कैसे करते हैं *और दुर्लभ कार्यों के लिए नामित कार्यों पर स्विच करते हैं।


12

selfDocumentingMethodName () ई की तुलना में कहीं बेहतर है ()

क्या यह? BEGIN और END की बजाय ब्लॉक डेलिमिटर के रूप में अधिकांश भाषाओं में {और} एक कारण है।

लोगों को थकाऊ पसंद है, और एक बार जब आप वाक्यविन्यास जानते हैं, तो लघु शब्दावली बेहतर होती है। अपने रेगेक्स उदाहरण की कल्पना करें यदि डी (अंक के लिए) 'डिजिट' था तो रीडगेक्स पढ़ने के लिए और भी भयावह होगा। यदि आपने इसे नियंत्रण पात्रों के साथ अधिक आसानी से पार्स करने योग्य बनाया है, तो यह XML की तरह दिखाई देगा। सिंटेक्स को जानने के बाद न तो उतना अच्छा है।

हालांकि आपके प्रश्न का ठीक से जवाब देने के लिए, आपको यह महसूस करना होगा कि regex उन दिनों से आता है जब terseness अनिवार्य था। एक 1 एमबी XML दस्तावेज़ को सोचना आसान है आज कोई बड़ी बात नहीं है, लेकिन हम उन दिनों के बारे में बात कर रहे हैं जब 1 एमबी बहुत अधिक था आपकी संपूर्ण भंडारण क्षमता। तब भी कम भाषाओं का उपयोग किया गया था, और रेगेक्स पर्ल या सी से एक लाख मील दूर नहीं है, इसलिए वाक्यविन्यास उस दिन के प्रोग्रामर से परिचित होगा जो वाक्यविन्यास सीखने से खुश होंगे। इसलिए इसे और अधिक क्रिया करने का कोई कारण नहीं था।


1
selfDocumentingMethodNameहै आम तौर पर सहमत की तुलना में बेहतर होने की eवजह से प्रोग्रामर अंतर्ज्ञान के साथ नहीं मिलाया गया है क्या वास्तव में पठनीयता या अच्छी गुणवत्ता कोड का गठन के मामले में वास्तविकता । सहमत होने वाले लोग गलत हैं, लेकिन यह है कि यह कैसे है।
लेउशेंको

1
@ लुशेंको: क्या आप दावा कर रहे हैं कि इससे e()बेहतर है selfDocumentingMethodName()?
जैक्सबीस

3
@JacquesB शायद सभी संदर्भों में नहीं (एक वैश्विक नाम की तरह)। लेकिन कसकर बँधी हुई चीजों के लिए? लगभग निश्चित रूप से। परंपरागत ज्ञान की तुलना में निश्चित रूप से अधिक बार।
लेउशेंको

1
@ लुशेंको: मेरे पास एक कठिन समय है कि एक संदर्भ की कल्पना करते हुए एक एकल पत्र फ़ंक्शन नाम एक अधिक वर्णनात्मक नाम से बेहतर है। लेकिन मुझे लगता है कि यह शुद्ध राय है।
जैक्सबी

1
@ माइल्सआउट: उदाहरण वास्तव में e()एक स्व-दस्तावेजीकरण विधि नाम के लिए है। क्या आप बता सकते हैं कि किस संदर्भ में वर्णनात्मक विधि नामों के बजाय एकल-अक्षर विधि नामों का उपयोग करना एक सुधार है?
जैकब

6

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

रेगेक्स लेगो टुकड़ों की तरह है। ऐसे कुछ तर्क हैं जिनका उपयोग किया जा सकता है, लेकिन विभिन्न रूपों में उनका उपयोग करने से लाखों अलग-अलग रेगेक्स पैटर्न बनेंगे, जिनका उपयोग अन्य जटिल कार्यों के लिए किया जा सकता है।

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

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

और इस अलग-अलग अद्वितीय कार्यों को संभालने के लिए पूर्व-परिभाषित तरीकों की पेशकश करना आसान नहीं है। आपके पास उन प्रकार के कार्यों के लिए स्ट्रिंग फ़ंक्शंस हैं, लेकिन यदि वे फ़ंक्शंस आपके निर्दिष्ट कार्य के लिए पर्याप्त नहीं हैं, तो यह regex का उपयोग करने का समय है


2

मैं जानता हूं कि यह सामर्थ्य के बजाय अभ्यास की समस्या है। समस्या आमतौर पर तब होती है जब एक समग्र प्रकृति संभालने के बजाय नियमित अभिव्यक्ति को सीधे लागू किया जाता है। इसी तरह, एक अच्छा प्रोग्रामर अपने कार्यक्रम के कार्यों को संक्षिप्त तरीकों से विघटित करेगा।

उदाहरण के लिए, URL के लिए एक रेगेक्स स्ट्रिंग लगभग से कम किया जा सकता है:

UriRe = [scheme][hier-part][query][fragment]

सेवा:

UriRe = UriSchemeRe + UriHierRe + "(/?|/" + UriQueryRe + UriFragRe + ")"
UriSchemeRe = [scheme]
UriHierRe = [hier-part]
UriQueryRe = [query]
UriFragRe = [fragment]

नियमित अभिव्यक्ति निफ्टी चीजें हैं, लेकिन वे उन लोगों द्वारा दुरुपयोग की संभावना रखते हैं जो अपनी स्पष्ट जटिलता में अवशोषित हो जाते हैं । परिणामी अभिव्यक्तियाँ लफ्फाजी हैं, दीर्घकालिक मूल्य से अनुपस्थित हैं।


2
दुर्भाग्य से अधिकांश प्रोग्रामिंग भाषाओं में कार्यक्षमता शामिल नहीं है जो कि रेक्सक्स की रचना करने में मदद करती है और जिस तरह से समूह पर कब्जा काम करता है वह रचना के लिए बहुत अनुकूल नहीं है।
कोडइन्चौस

1
अन्य भाषाओं को अपने "पर्ल संगत नियमित अभिव्यक्ति" समर्थन में पर्ल 5 को पकड़ने की जरूरत है। Subexpressions regex स्पेसिफिकेशन के स्ट्रेंथनिंग के समान नहीं हैं। कैप्टेंसी का नाम होना चाहिए, न कि निहित संख्या पर भरोसा करना।
जडलुगोज़ सिप

0

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

पर्ल व्हाट्सएप और टिप्पणियों की अनुमति देकर उन्हें अधिक पठनीय बनाने के बजाय एक कमजोर प्रयास करता है, लेकिन दूरस्थ रूप से कल्पनाशील कुछ भी नहीं करता है।

अन्य वाक्यविन्यास हैं। एक अच्छा एक regexps के लिए scsh सिंटैक्स है , जो मेरे अनुभव में regexps का उत्पादन करता है जो टाइप करने के लिए यथोचित आसान हैं, लेकिन तथ्य के बाद भी पठनीय हैं।

[ scsh अन्य कारणों से शानदार है, जिसमें से एक इसका प्रसिद्ध स्वीकार पाठ है ]


2
Perl6 करता है! व्याकरण को देखो।
JDługosz

@ JDługosz जहाँ तक मैं देख सकता हूँ, कि पार्सर जनरेटर के लिए एक तंत्र की तरह दिखता है, बजाय नियमित अभिव्यक्ति के लिए एक वैकल्पिक वाक्यविन्यास के बजाय। लेकिन भेद शायद एक गहरा नहीं है।
नॉर्मन ग्रे

यह एक प्रतिस्थापन हो सकता है, लेकिन एक ही शक्ति तक सीमित नहीं है। आप मॉडिफायर्स के 1 से 1 पत्राचार के साथ एक इनलाइन व्याकरण में एक regedp का अनुवाद कर सकते हैं लेकिन अधिक पठनीय वाक्यविन्यास में। इसे बढ़ावा देने वाले उदाहरण मूल पर्ल सर्वनाश में हैं।
जडलुगोज़ सिप

0

मेरा मानना ​​है कि नियमित-अभिव्यक्तियों को 'सामान्य' और यथासंभव सरल बनाया गया था, इसलिए उन्हें कहीं भी इस्तेमाल किया जा सकता है।

आप regex.isRange(..).followedBy(..)एक विशिष्ट प्रोग्रामिंग भाषा के सिंटैक्स और शायद ऑब्जेक्ट-ओरिएंटेड स्टाइल (विधि-निर्धारण) दोनों के लिए युग्मित हैं।

उदाहरण के लिए C में यह सटीक 'regex' कैसा दिखेगा? कोड बदलना होगा।

सबसे 'सामान्य' दृष्टिकोण एक सरल संक्षिप्त भाषा को परिभाषित करना होगा जो तब बिना किसी अन्य भाषा में आसानी से परिवर्तित किया जा सकता है। और यह (लगभग) क्या regex हैं।


0

पर्ल-कम्पेटिबल रेगुलर एक्सप्रेशन इंजन का व्यापक रूप से उपयोग किया जाता है, जो एक नियमित रेग्युलर सिंटैक्स प्रदान करता है जिसे कई संपादक और भाषाएं समझती हैं। जैसा कि @ JDługosz ने टिप्पणी में बताया, पर्ल 6 (पर्ल 5 का एक नया संस्करण नहीं है, लेकिन पूरी तरह से अलग भाषा) ने व्यक्तिगत अभिव्यक्तियों के तत्वों से उन्हें बनाकर नियमित अभिव्यक्ति को अधिक पठनीय बनाने का प्रयास किया है। उदाहरण के लिए, यहाँ एक उदाहरण व्याकरण से यूआरएल प्राप्त करने के लिए व्याकरण है :

grammar URL {
  rule TOP {
    <protocol>'://'<address>
  }
  token protocol {
    'http'|'https'|'ftp'|'file'
  }
  rule address {
    <subdomain>'.'<domain>'.'<tld>
  }
  ...
}

इस तरह से नियमित अभिव्यक्ति को विभाजित करने से प्रत्येक बिट को व्यक्तिगत रूप से परिभाषित किया जा सकता है (जैसे domainकि अल्फ़ान्यूमेरिक होने के लिए विवश होना) या उपक्लासिंग के माध्यम से विस्तारित किया जाता है (जैसे FileURL is URLकि protocolकेवल करने के लिए विवश होना "file")।

तो: नहीं, नियमित अभिव्यक्तियों की थकावट का कोई तकनीकी कारण नहीं है, लेकिन नए, क्लीनर और अधिक पठनीय तरीके उनका प्रतिनिधित्व करने के लिए पहले से ही यहाँ हैं! इसलिए उम्मीद है कि हम इस क्षेत्र में कुछ नए विचार देखेंगे।

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