มุมมองทางประวัติศาสตร์
บทความ Wikipediaค่อนข้างละเอียดเกี่ยวกับต้นกำเนิดของนิพจน์ทั่วไป (Kleene, 1956) ไวยากรณ์เดิมค่อนข้างง่ายมีเพียง*
, +
, ?
, และการจัดกลุ่ม|
(...)
มันสั้น ( และสามารถอ่านได้ทั้งสองไม่จำเป็นต้องคัดค้าน) เพราะภาษาทางการมักจะแสดงด้วยสัญกรณ์คณิตศาสตร์สั้น ๆ
ต่อมาไวยากรณ์และความสามารถในการพัฒนากับบรรณาธิการและขึ้นอยู่กับPerlซึ่งได้รับการพยายามที่จะรวบรัดโดยการออกแบบ ( "การก่อสร้างทั่วไปควรจะสั้น" ) สิ่งนี้ทำให้ไวยากรณ์ซับซ้อนขึ้นมาก แต่โปรดทราบว่าขณะนี้ผู้คนคุ้นเคยกับการแสดงออกปกติและสามารถเขียนได้ดี (ถ้าไม่อ่าน) ความจริงที่ว่าบางครั้งพวกเขาเขียนเท่านั้นแนะนำว่าเมื่อพวกเขายาวเกินไปพวกเขามักจะไม่ใช่เครื่องมือที่เหมาะสม
นิพจน์ทั่วไปมักไม่สามารถอ่านได้เมื่อถูกทารุณกรรม
เกินกว่าการแสดงออกปกติตามสตริง
พูดเกี่ยวกับไวยากรณ์ทางเลือกลองดูที่มีอยู่แล้ว ( cl-ppcreในCommon LISP ) นิพจน์ทั่วไปที่ยาวของคุณสามารถแยกวิเคราะห์ได้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)
ไวยากรณ์นี้เป็น verbose มากขึ้นและถ้าคุณดูความคิดเห็นด้านล่างไม่จำเป็นต้องอ่านเพิ่มเติม จึงไม่คิดว่าเพราะคุณมีไวยากรณ์ที่มีขนาดกะทัดรัดน้อยกว่าสิ่งที่จะชัดเจนโดยอัตโนมัติ
อย่างไรก็ตามหากคุณเริ่มมีปัญหากับนิพจน์ทั่วไปการเปลี่ยนเป็นรูปแบบนี้อาจช่วยให้คุณถอดรหัสและตรวจแก้รหัสของคุณได้ นี่เป็นข้อดีอย่างหนึ่งของรูปแบบสตริงที่ข้อผิดพลาดของอักขระเดียวสามารถมองเห็นได้ยาก
ประโยชน์หลักของไวยากรณ์นี้คือการจัดการกับนิพจน์ทั่วไปโดยใช้รูปแบบที่มีโครงสร้างแทนการเข้ารหัสแบบสตริง ที่ช่วยให้คุณสามารถเขียนและสร้างนิพจน์เช่นโครงสร้างข้อมูลอื่น ๆ ในโปรแกรมของคุณ เมื่อฉันใช้ไวยากรณ์ข้างต้นนี้เป็นเพราะฉันต้องการสร้างการแสดงออกจากส่วนเล็ก ๆ (ดูคำตอบ CodeGolf ของฉัน ) สำหรับตัวอย่างของคุณเราอาจเขียน1 :
`(:sequence
:start-anchor
,(protocol)
,(slashes)
,(domain)
,(top-level-domain) ... )
นอกจากนี้ยังสามารถสร้างนิพจน์ทั่วไปที่ใช้สตริงได้โดยใช้การต่อสตริงและหรือการแก้ไขในฟังก์ชันตัวช่วย แต่มีข้อ จำกัด ที่มีกิจวัตรสตริงซึ่งมีแนวโน้มที่จะถ่วงรหัส (คิดว่าเกี่ยวกับปัญหาการทำรังไม่แตกต่างจาก backticks เทียบกับ$(...)
ในทุบตี; ยังหลบหนีตัวละครอาจทำให้คุณปวดหัว)
โปรดทราบว่าแบบฟอร์มด้านบนอนุญาตให้ใช้(:regex "string")
แบบฟอร์มเพื่อให้คุณสามารถผสมเครื่องหมายคำย่อกับต้นไม้ ทั้งหมดนี้นำไปสู่ IMHO ในการอ่านและเรียงความที่ดี มันระบุถึงปัญหาสามข้อที่แสดงโดย delnanทางอ้อม (กล่าวคือไม่ได้อยู่ในภาษาของการแสดงออกปกติ)
สรุป
เพื่อจุดประสงค์ส่วนใหญ่สัญกรณ์สั้น ๆ ในความเป็นจริงสามารถอ่านได้ มีปัญหาเมื่อต้องรับมือกับความหมายที่เพิ่มขึ้นซึ่งเกี่ยวข้องกับการย้อนรอย ฯลฯ แต่การใช้ของพวกเขานั้นไม่ค่อยเป็นธรรม การใช้นิพจน์ทั่วไปที่ไม่มีการรับประกันสามารถนำไปสู่นิพจน์ที่ไม่สามารถอ่านได้
นิพจน์ทั่วไปไม่จำเป็นต้องเข้ารหัสเป็นสตริง หากคุณมีไลบรารีหรือเครื่องมือที่สามารถช่วยคุณสร้างและเขียนนิพจน์ทั่วไปคุณจะหลีกเลี่ยงข้อบกพร่องที่อาจเกิดขึ้นมากมายที่เกี่ยวข้องกับการจัดการสตริง
อีกทางเลือกหนึ่งไวยากรณ์แบบเป็นทางการสามารถอ่านได้มากขึ้นและดีกว่าในการตั้งชื่อและสรุปย่อยการแสดงออก โดยทั่วไปเทอร์มินัลจะแสดงเป็นนิพจน์ทั่วไปอย่างง่าย
1.คุณอาจต้องการสร้างนิพจน์ของคุณในเวลาอ่านเนื่องจากนิพจน์ทั่วไปมักจะเป็นค่าคงที่ในแอปพลิเคชัน ดูcreate-scanner
และload-time-value
:
'(:sequence :start-anchor #.(protocol) #.(slashes) ... )