“ ตอนนี้คุณมีปัญหาสองประการ” หมายความว่าอย่างไร


200

มีคำพูดยอดนิยมโดยJamie Zawinski :

บางคนเมื่อเผชิญกับปัญหาคิดว่า "ฉันรู้ฉันจะใช้สำนวนปกติ" ตอนนี้พวกเขามีสองปัญหา

ควรเข้าใจคำพูดนี้อย่างไร


46
ปัญหาที่สองคือพวกเขาใช้ regex และยังไม่ได้แก้ไขปัญหาแรกดังนั้น 2 ปัญหา
Ampt

24
@Eparhoric - จริง ๆ แล้วรหัสที่ดีนั้นสั้น - แต่ไม่ต้องรัดกุม
Steve314

24
@IQAndreas: ฉันคิดว่ามันตั้งใจที่จะกึ่งตลก ความคิดเห็นที่กำลังทำอยู่คือถ้าคุณไม่ระวังการใช้นิพจน์ทั่วไปสามารถทำให้สิ่งต่าง ๆ แย่ลงแทนที่จะดีกว่า
FrustratedWithFormsDesigner

145
บางคนเมื่อพยายามอธิบายบางสิ่งคิดว่า "ฉันรู้ฉันจะใช้คำพูดของ Jamie Zawinski" ตอนนี้พวกเขามีสองสิ่งที่ต้องอธิบาย
Detly

คำตอบ:


220

บางเทคโนโลยีการเขียนโปรแกรมได้โดยทั่วไปดีเข้าใจโดยโปรแกรมเมอร์ ( การแสดงออกปกติ , จุดลอย , Perl , AWK , IoC ... และอื่น ๆ )

สิ่งเหล่านี้สามารถเป็นเครื่องมือที่ทรงพลังอย่างน่าอัศจรรย์สำหรับการแก้ปัญหาที่เหมาะสม การแสดงออกปกติโดยเฉพาะอย่างยิ่งมีประโยชน์มากสำหรับการจับคู่ภาษาปกติ และมีปมปัญหาอยู่: มีคนไม่กี่คนที่รู้วิธีอธิบายภาษาปกติ (เป็นส่วนหนึ่งของทฤษฎีวิทยาการคอมพิวเตอร์ / ภาษาศาสตร์ที่ใช้สัญลักษณ์ตลก - คุณสามารถอ่านเกี่ยวกับเรื่องนี้ในลำดับชั้นของ Chomsky )

เมื่อต้องรับมือกับสิ่งเหล่านี้ถ้าคุณใช้มันผิดมันไม่น่าเป็นไปได้ที่คุณจะแก้ไขปัญหาดั้งเดิมของคุณได้ การใช้นิพจน์ทั่วไปเพื่อจับคู่ HTML (เหตุการณ์ที่เกิดขึ้นบ่อยเกินไป) จะหมายความว่าคุณจะพลาดตัวพิมพ์ของขอบ และตอนนี้คุณยังคงมีปัญหาดั้งเดิมที่คุณไม่ได้แก้ปัญหาและข้อผิดพลาดเล็ก ๆ อีกหนึ่งที่ลอยอยู่รอบ ๆ ซึ่งได้รับการแนะนำโดยใช้วิธีการแก้ปัญหาที่ไม่ถูกต้อง

นี่ไม่ได้เป็นการบอกว่าไม่ควรใช้การแสดงออกปกติ แต่ควรจะใช้เพื่อทำความเข้าใจว่าชุดของปัญหาที่พวกเขาสามารถแก้ไขได้และไม่สามารถแก้ไขและใช้งานได้อย่างรอบคอบ

กุญแจสำคัญในการบำรุงรักษาซอฟต์แวร์คือการเขียนรหัสที่สามารถบำรุงรักษาได้ การใช้นิพจน์ทั่วไปสามารถตอบโต้เป้าหมายนั้นได้ เมื่อทำงานกับนิพจน์ทั่วไปคุณได้เขียนคอมพิวเตอร์ขนาดเล็ก (โดยเฉพาะอย่างยิ่งสถานะ จำกัด อัตโนมัติของคอมพิวเตอร์ ) ในภาษาเฉพาะโดเมน ง่ายต่อการเขียน 'Hello world' ในภาษานี้และได้รับความมั่นใจในพื้นฐาน แต่จะต้องมีความเข้าใจในภาษาปกติมากขึ้นเพื่อหลีกเลี่ยงการเขียนข้อบกพร่องเพิ่มเติมที่ยากที่จะระบุและแก้ไข (เพราะ พวกเขาไม่ได้เป็นส่วนหนึ่งของโปรแกรมที่มีนิพจน์ทั่วไปอยู่)

ดังนั้นตอนนี้คุณมีปัญหาใหม่ คุณเลือกเครื่องมือของการแสดงออกปกติเพื่อแก้ปัญหา (เมื่อมันไม่เหมาะสม) และคุณมีข้อบกพร่องสองข้อในขณะนี้ซึ่งทั้งสองอย่างนั้นยากที่จะหาเพราะพวกมันซ่อนอยู่ในอีกชั้นหนึ่งของสิ่งที่เป็นนามธรรม


8
ผมไม่แน่ใจว่า Perl ตัวเองอยู่ในรายชื่อของเทคโนโลยีที่ไม่ดีที่เข้าใจกันโดยโปรแกรมเมอร์;)
ซีดีการ์ด

21
@crad มันยิ่งกว่าที่มีการพูดถึงเรื่อง perl ด้วย ... หลายคนเคยได้ยินว่ามันโด่งดังที่นั่น ฉันยังคงชอบจุดลอยหนึ่งในการพูดคุย rand: "ตอนนี้คุณมีปัญหา 2.00000152"

56
@Crad บางคนเมื่อเผชิญหน้ากับปัญหาคิดว่า "ฉันรู้ว่าฉันจะใช้ Perl" ตอนนี้พวกเขามีปัญหา $ (^ @ #% () ^%) (#)
Michael Hampton

4
@Jens ถ้ามีอะไรเพิ่มเติมพลังของ PCRE เทียบกับ regex แบบดั้งเดิมทำให้มันเป็นทางออกที่ดึงดูดมากขึ้นและยากที่จะรักษาไว้ ออโตไฟไนต์ที่ PCRE จับคู่นั้นได้ถูกสำรวจในการขยายไฟไนต์ออโตมาตะเพื่อจับคู่นิพจน์ปกติที่เข้ากันได้กับ Perl ได้อย่างมีประสิทธิภาพ ... และมันเป็นสิ่งที่ไม่สำคัญ อย่างน้อยกับ regex ดั้งเดิมเราสามารถเข้าใจได้โดยไม่มีปัญหามากเกินไปเมื่อเข้าใจแนวคิดที่จำเป็น

6
คุณทำคะแนนได้ดี นิพจน์ทั่วไปนั้นเป็นภาษาที่สองและไม่ใช่เรื่องง่าย แม้ว่าโปรแกรมเมอร์ดั้งเดิมจะมีความสามารถในภาษาหลักและรสชาติของ regex ที่ใช้เพิ่มใน "ภาษาที่สอง" หมายถึงอัตราต่อรองที่ต่ำกว่าที่ผู้ดูแลจะรู้ทั้งสอง ไม่ต้องพูดถึงว่าการอ่าน regex มักจะต่ำกว่าภาษา "โฮสต์"
JS

95

นิพจน์ทั่วไป - โดยเฉพาะอย่างยิ่งสิ่งที่ไม่สำคัญ - อาจยากต่อการเข้าใจและบำรุงรักษา คุณเพียงแค่ต้องดูจำนวนคำถามใน Stack Overflow ที่ติดแท็ก[regex]ที่ผู้ถามได้สันนิษฐานว่าคำตอบสำหรับปัญหาของพวกเขาคือ regex และติดอยู่ในภายหลัง ในหลายกรณีปัญหาสามารถแก้ไขได้ (และควร) ด้วยวิธีอื่น

ซึ่งหมายความว่าหากคุณตัดสินใจใช้ regex ตอนนี้คุณมีปัญหาสองประการ:

  1. ปัญหาดั้งเดิมที่คุณต้องการแก้ไข
  2. การสนับสนุนของ regex

โดยพื้นฐานแล้วฉันคิดว่าเขาหมายความว่าคุณควรใช้ regex หากไม่มีวิธีอื่นในการแก้ปัญหาของคุณ โซลูชันอื่นอาจจะง่ายกว่าในการเขียนโค้ดบำรุงรักษาและสนับสนุน อาจช้าลงหรือมีประสิทธิภาพน้อยลง แต่ถ้าหากไม่ใช่เรื่องง่ายในการบำรุงรักษาและการสนับสนุนควรเป็นเรื่องที่น่ากังวล


27
และที่แย่กว่านั้นคือพวกเขามีพลังมากพอที่จะหลอกผู้คนให้ลองใช้พวกมันเพื่อแยกวิเคราะห์สิ่งที่พวกเขาทำไม่ได้อย่างเช่น HTML ดูคำถามมากมายเกี่ยวกับ SO บน "ฉันจะแยก HTML ได้อย่างไร"
Frank Shearar

6
สำหรับบางสถานการณ์ regex นั้นยอดเยี่ยม ในหลายกรณีไม่มากนัก ในอีกด้านหนึ่งมันเป็นหลุมแห่งความสิ้นหวัง ปัญหามักเกิดขึ้นเมื่อมีคนเรียนรู้เกี่ยวกับพวกเขาเป็นครั้งแรกและเริ่มเห็นแอปพลิเคชันทุกที่ อีกคำพูดที่มีชื่อเสียง: "เมื่อเครื่องมือเดียวที่คุณมีคือค้อนทุกอย่างดูเหมือนเล็บ"
ทอดด์วิลเลียมสัน

3
นี่หมายความว่าด้วยจำนวนคำถามในแท็ก SO [c #] มันเป็นภาษาการเขียนโปรแกรมที่ยากที่สุดที่จะเข้าใจหรือไม่?

2
ฉันค่อนข้างจะเห็นการแสดงออกปกติที่ซับซ้อนกว่าชุดยาวของการโทรไปยังวิธีการสตริง OTOH ฉันเกลียดการดูการแสดงออกปกติที่ใช้ในการแยกวิเคราะห์ภาษาที่ซับซ้อน
วินไคลน์

5
"โดยพื้นฐานแล้วฉันคิดว่าเขาหมายความว่าคุณควรใช้ regex หากไม่มีวิธีอื่นในการแก้ปัญหาของคุณเท่านั้นทางออกอื่น ๆ จะง่ายกว่าในการเขียนโค้ดรักษาและสนับสนุน" - ไม่เห็นด้วยอย่างจริงจัง .. Regexes เป็นเครื่องมือที่ยอดเยี่ยมคุณเพียงแค่ต้องรู้ขอบเขตของมัน งานจำนวนมากสามารถเขียนรหัสได้อย่างหรูหรายิ่งขึ้นด้วย regexes (แต่เพื่อเป็นตัวอย่างคุณไม่ควรใช้มันเพื่อแยกวิเคราะห์ HTML)
Karoly Horvath

69

ส่วนใหญ่มันเป็นเรื่องตลกที่ปากลิ้นแก้มแม้ว่าจะมีเม็ดความจริง

มีงานบางอย่างที่การแสดงผลปกติเหมาะอย่างยิ่ง ฉันเคยแทนที่โค้ดตัวแยกวิเคราะห์โคตร recursive descent ที่เขียนด้วยตนเองด้วยนิพจน์ทั่วไปหนึ่งนิพจน์ซึ่งใช้เวลาประมาณ 10 นาทีในการดีบักอย่างสมบูรณ์ ผู้คนบอกว่า regexes นั้นยากที่จะเข้าใจและตรวจแก้จุดบกพร่อง แต่สิ่งที่ใช้อย่างเหมาะสมนั้นไม่ยากที่จะทำการ debug ในฐานะ parser ที่ออกแบบด้วยมือขนาดใหญ่ ในตัวอย่างของฉันใช้เวลาสองสัปดาห์ในการดีบักเคสขอบทั้งหมดของโซลูชันที่ไม่ใช่ regex

อย่างไรก็ตามการถอดความลุงเบ็น:

ด้วยการแสดงออกที่ดีมาพร้อมความรับผิดชอบที่ดี

กล่าวอีกนัยหนึ่ง regexes เพิ่มความชัดเจนให้กับภาษาของคุณ แต่นั่นทำให้ผู้รับผิดชอบมากขึ้นในการเลือกโหมดการแสดงออกที่อ่านง่ายที่สุดสำหรับงานที่กำหนด

บางสิ่งบางอย่างในตอนแรกดูเหมือนเป็นงานที่ดีสำหรับการแสดงออกปกติ แต่ไม่ได้ ตัวอย่างเช่นสิ่งใดก็ตามที่มีโทเค็นที่ซ้อนกันเช่น HTML บางครั้งคนใช้นิพจน์ทั่วไปเมื่อวิธีที่ง่ายกว่าชัดเจนกว่า ตัวอย่างเช่นstring.endsWith("ing")เข้าใจง่ายกว่า regex ที่เทียบเท่า บางครั้งผู้คนพยายามยัดเยียดปัญหาใหญ่ให้เป็น regex เดียวโดยที่การแบ่งเป็นชิ้น ๆ มีความเหมาะสมมากกว่า บางครั้งผู้คนล้มเหลวในการสร้าง abstractions ที่เหมาะสมทำซ้ำ regex ซ้ำแล้วซ้ำอีกแทนที่จะสร้างฟังก์ชันที่มีชื่อดีเพื่อทำงานเดียวกัน (อาจนำไปใช้ภายในกับ regex)

ด้วยเหตุผลบางอย่าง regexes มีแนวโน้มแปลก ๆ ที่จะสร้างจุดบอดให้กับหลักการทางวิศวกรรมซอฟต์แวร์ทั่วไปเช่นความรับผิดชอบเดี่ยวและ DRY นั่นเป็นเหตุผลที่แม้แต่คนที่รักพวกเขายังพบปัญหาอยู่ตลอดเวลา


10
ลุงเบ็นไม่ได้พูดด้วยเช่นกัน บางทีนั่นอาจเป็นสาเหตุที่ทำให้ผู้คนเริ่มมีความสุขกับ regexes ...
Andrzej Doyle

4
ปัญหาเกี่ยวกับ regex เกี่ยวกับ HTML ที่พัฒนานักพัฒนาที่ไม่มีประสบการณ์คือ HTML มีไวยากรณ์ที่ไม่มีบริบทไม่ใช่ regex สามารถใช้สำหรับการแยกวิเคราะห์ HTML (หรือ XML) อย่างง่าย (เช่นการดึง URL จากแท็ก anchor ที่ระบุชื่อ) แต่ ไม่เหมาะสำหรับสิ่งที่ซับซ้อน สำหรับการแยกวิเคราะห์ DOM นั้นเหมาะสมกว่า การอ่านที่เกี่ยวข้อง: ชัมลำดับชั้น

53

Jeff Atwood นำเสนอการตีความที่แตกต่างออกไปในบล็อกโพสต์ที่พูดถึงสิ่งนี้มาก: นิพจน์ทั่วไป: ตอนนี้คุณมีสองปัญหา (ขอบคุณEuphoricสำหรับลิงค์)

การวิเคราะห์ข้อความทั้งหมดของกระทู้ของเจมี่ในกระทู้ 1997 ต้นฉบับเราพบสิ่งต่อไปนี้:

ธรรมชาติของ Perl ส่งเสริมให้ใช้การแสดงออกปกติเกือบจะยกเว้นเทคนิคอื่น ๆ ทั้งหมด พวกเขาอยู่ไกลและไกลที่สุด "ชัดเจน" (อย่างน้อยที่สุดสำหรับคนที่ไม่รู้จักดี) วิธีที่จะได้รับจากจุด A ถึงจุด B

คำพูดแรกจะกะล่อนเกินไปที่จะดำเนินการอย่างจริงจัง แต่นี่ฉันเห็นด้วยอย่างสมบูรณ์ นี่คือประเด็นที่เจมี่พยายามทำไม่ใช่ว่าการแสดงผลปกติเป็นสิ่งที่ชั่วร้าย แต่สำหรับการแสดงออกปกติมากเกินไปนั้นเป็นสิ่งที่ชั่วร้าย

แม้ว่าคุณจะเข้าใจนิพจน์ทั่วไปอย่างถ่องแท้แล้วก็ตามคุณก็พบปัญหาGolden Hammerพยายามแก้ปัญหาด้วยนิพจน์ทั่วไปเมื่อมันง่ายกว่าและชัดเจนกว่าที่จะทำสิ่งเดียวกันด้วยรหัสปกติ (ดูที่CodingHorror: Regex use เทียบกับการละเมิด Regex )

มีการโพสต์บล็อกอีกอันหนึ่งซึ่งดูที่บริบทของการอ้างถึงและมีรายละเอียดมากกว่า Atwood: บล็อกของ Jeffrey Friedl: ที่มาของชื่อดัง“ ตอนนี้คุณมีปัญหาสองข้อ”


3
นี่คือคำตอบที่ดีที่สุดในใจของฉันเพราะมันเพิ่มบริบท คำติชมของ jwz เกี่ยวกับ regexes นั้นเกี่ยวกับ Perl มากเท่ากับอะไร
Evicatos

3
@Evicatos มีการทำวิจัยมากยิ่งขึ้นในหัวข้อ 1997 เดียวกันในบล็อกโพสต์อื่น: regex.info/blog/2006-09-15/247
IQAndreas

30

มีบางสิ่งเกิดขึ้นกับคำพูดนี้

  1. อ้างเป็นปรับย้อนหลังของเรื่องตลกก่อนหน้านี้:

    เมื่อใดก็ตามที่ประสบปัญหาบางคนพูดว่า "ให้ใช้ AWK" ตอนนี้พวกเขามีสองปัญหา - D. Tilbrook

    มันเป็นเรื่องตลกและขุดจริง แต่ก็ยังเป็นวิธีการเน้น regex เป็นวิธีที่ไม่ดีโดยการเชื่อมโยงกับโซลูชั่นที่ไม่ดีอื่น ๆ มันยอดเยี่ยมฮ่าร้ายแรงเพียงสักครู่

  2. สำหรับฉัน - โปรดจำไว้ว่าคำพูดนี้เปิดกว้างสำหรับการตีความ - ความหมายนั้นตรงไปตรงมา เพียงแค่ประกาศความคิดในการใช้นิพจน์ทั่วไปไม่ได้แก้ปัญหา นอกจากนี้คุณได้เพิ่มความซับซ้อนทางปัญญาของรหัสด้วยการเพิ่มภาษาเพิ่มเติมด้วยกฎที่แตกต่างจากภาษาที่คุณใช้

  3. แม้ว่าตลกเป็นเรื่องตลก แต่คุณต้องเปรียบเทียบความซับซ้อนของโซลูชันที่ไม่ใช่ regex กับความซับซ้อนของโซลูชัน regex + ความซับซ้อนเพิ่มเติมของการรวม regexes มันอาจจะคุ้มค่าในการแก้ปัญหากับ regex แม้จะมีค่าใช้จ่ายเพิ่มเติมในการเพิ่ม regexes


21

RegularExpressionsarenoworsetoreadormaintainthananyotherunformattedcontent; indeedaregexisprobablyeasiertoreadthanthispieceoftexthere-butunfortunatelytheyhaveabadreputationbecausesomeimplementationsdon'tallowformattingandpeopleingeneraldon'tknowthatyoucandoit

(นิพจน์ทั่วไปไม่เลวร้ายกว่าที่จะอ่านหรือดูแลรักษากว่าเนื้อหาที่ไม่ได้จัดรูปแบบอื่น ๆ แน่นอนว่า regex นั้นอาจอ่านได้ง่ายกว่าข้อความชิ้นนี้ที่นี่ - แต่น่าเสียดายที่พวกเขามีชื่อเสียงไม่ดีเพราะการใช้งานบางอย่างไม่อนุญาต ไม่ทราบว่าคุณสามารถทำได้)


นี่เป็นตัวอย่างเล็กน้อย:

^(?:[^,]*+,){21}[^,]*+$


ซึ่งไม่ใช่เรื่องยากที่จะอ่านหรือดูแลรักษา แต่ก็ง่ายกว่าเมื่อดูเช่นนี้:

(?x)    # enables comments, so this whole block can be used in a regex.
^       # start of string

(?:     # start non-capturing group
  [^,]*+  # as many non-commas as possible, but none required
  ,       # a comma
)       # end non-capturing group
{21}    # 21 of previous entity (i.e. the group)

[^,]*+  # as many non-commas as possible, but none required

$       # end of string

นั่นเป็นตัวอย่างเล็กน้อย (การแสดงความคิดเห็น$คล้ายกับการแสดงความคิดเห็นi++) แต่ชัดเจนว่าไม่ควรมีปัญหาในการอ่านทำความเข้าใจและการดูแลรักษา


ตราบใดที่คุณชัดเจนว่าเมื่อใดที่การแสดงออกปกติเหมาะสมและเมื่อพวกเขาเป็นความคิดที่ดีไม่มีอะไรผิดปกติกับพวกเขาและส่วนใหญ่เวลาที่คำพูด JWZ ใช้ไม่ได้จริง ๆ


1
แน่นอน แต่ฉันไม่ได้มองหาการอภิปรายเกี่ยวกับข้อดีของ regexs และฉันไม่ต้องการเห็นการสนทนานี้เป็นไปในทางนั้น ฉันแค่พยายามที่จะเข้าใจสิ่งที่เขาได้รับ
Paul Biggar

1
จากนั้นลิงก์ในความคิดเห็นของ livibetter จะบอกสิ่งที่คุณต้องรู้ การตอบสนองนี้เป็นเพียงการชี้ให้เห็นว่า regexes ไม่จำเป็นต้องคลุมเครือและทำให้การอ้างอิงเป็นเรื่องไร้สาระ
Peter Boughton

8
ประเด็นของการใช้*+คืออะไร? มันแตกต่างกันอย่างไร (ตามหน้าที่) จาก*?
Timwi

1
แม้ว่าสิ่งที่คุณพูดอาจเป็นจริง แต่ก็ไม่ได้ตอบคำถามเฉพาะนี้ คำตอบของคุณเพิ่มขึ้นถึง "ในความคิดของฉันว่าคำพูดมักไม่เป็นความจริง" คำถามไม่ได้เกี่ยวกับว่ามันเป็นเรื่องจริงหรือไม่ แต่สิ่งที่อ้างถึงความหมาย
Bryan Oakley

2
*+ในกรณีนี้ไม่มีประโยชน์ที่จะทำ ทุกอย่างได้รับการยึดและสามารถจับคู่ในการส่งผ่านครั้งเดียวโดยหุ่นยนต์ที่สามารถนับได้ถึง 22 ตัวดัดแปลงที่ถูกต้องของชุดที่ไม่ใช่จุลภาคนั้นเก่าเพียง*อย่างเดียว (ยิ่งไปกว่านั้นก็ไม่ควรมีความแตกต่างระหว่างอัลกอริธึมการจับคู่โลภและไม่โลภที่นี่มันเป็นกรณีที่ง่ายมาก)
Donal Fellows

14

นอกเหนือจากคำตอบของ ChrisF - การแสดงออกปกติ "นั้นยากที่จะเขียนรหัสทำความเข้าใจและดูแลรักษา" ยิ่งแย่ไปกว่านั้นคือพวกมันมีพลังมากพอที่จะหลอกผู้คนให้ลองใช้มันเพื่อแยกวิเคราะห์สิ่งที่ไม่สามารถทำได้เช่น HTML ดูคำถามมากมายเกี่ยวกับ SO บน "ฉันจะแยก HTML ได้อย่างไร" ยกตัวอย่างเช่นคำตอบเดียวที่ยิ่งใหญ่ที่สุดใน SO!


14

นิพจน์ทั่วไปนั้นทรงพลังมาก แต่ก็มีปัญหาเล็ก ๆ ปัญหาหนึ่ง เขียนยากและใกล้อ่านไม่ได้

ในกรณีที่ดีที่สุดการใช้นิพจน์ทั่วไปจะช่วยแก้ปัญหาดังนั้นคุณจึงมีปัญหาในการบำรุงรักษาโค้ดที่ซับซ้อนเท่านั้น หากคุณไม่ได้รับนิพจน์ปกติที่ถูกต้องคุณมีทั้งปัญหาดั้งเดิมและปัญหาเกี่ยวกับรหัสที่อ่านไม่ได้ซึ่งใช้งานไม่ได้

บางครั้งนิพจน์ทั่วไปจะเรียกว่ารหัสแบบเขียนอย่างเดียว ต้องเผชิญกับการแสดงออกปกติที่ต้องมีการแก้ไขก็มักจะเริ่มต้นจากศูนย์ได้เร็วกว่าการพยายามที่จะเข้าใจการแสดงออก


1
ปัญหาที่แท้จริงคือ regexps ไม่สามารถใช้งานได้เช่น parser เนื่องจากพวกเขาไม่สามารถนับได้ว่าพวกมันอยู่ลึกแค่ไหน

4
@ Thorbjørn Ravn Andersen: นั่นเป็นข้อ จำกัด มากกว่าปัญหา เป็นเพียงปัญหาถ้าคุณพยายามใช้นิพจน์ทั่วไปสำหรับสิ่งนั้นและไม่ใช่ปัญหากับนิพจน์ทั่วไปเป็นปัญหากับวิธีการที่คุณเลือก
Guffa

1
คุณสามารถใช้ REs ได้ดีสำหรับ lexer (ดีสำหรับภาษาส่วนใหญ่) แต่การรวมโทเค็นสตรีมเข้ากับต้นไม้แยกวิเคราะห์ (เช่นการแยกวิเคราะห์ ) นั้นอยู่เหนือกว่าพวกมัน
Donal Fellows

10

ปัญหาคือว่า regex เป็นสัตว์ร้ายที่ซับซ้อนและคุณจะแก้ปัญหาของคุณถ้าคุณใช้ regex อย่างสมบูรณ์ หากไม่เป็นเช่นนั้นคุณจะพบปัญหา 2 ประการ ได้แก่ ปัญหาดั้งเดิมและปัญหา regex

คุณอ้างว่ามันสามารถใช้งานโค้ดได้ร้อยบรรทัด แต่คุณสามารถโต้แย้งได้ว่าโค้ดที่กระชับและชัดเจน 100 บรรทัดนั้นดีกว่า regex หนึ่งบรรทัด

หากคุณต้องการหลักฐานนี้: คุณสามารถตรวจสอบSO Classicนี้หรือเพียงแค่หวีผ่านแท็ก SO Regex


8
การอ้างสิทธิ์ในประโยคแรกของคุณไม่เป็นความจริง Regex นั้นไม่ซับซ้อนเป็นพิเศษและคุณไม่จำเป็นต้องใช้เครื่องมืออื่นใดในการแก้ไขปัญหาอีกต่อไป นั่นเป็นเพียง FUD ย่อหน้าที่สองของคุณไร้สาระแน่นอน : คุณสามารถโต้แย้งได้ แต่มันไม่ดีเลย
Konrad Rudolph

1
@ KonradRudolph ฉันคิดว่าข้อเท็จจริงที่ว่ามีการสร้าง regex จำนวนมากและเครื่องมือตรวจสอบความถูกต้องไปแสดงว่า regex เป็นกลไกที่ซับซ้อน ไม่ใช่มนุษย์สามารถอ่านได้ (โดยการออกแบบ) และสามารถทำให้เกิดการเปลี่ยนแปลงอย่างสมบูรณ์สำหรับผู้ที่แก้ไขหรือเขียนโค้ดที่ใช้ regex ในส่วนที่สองฉันคิดว่ามันชัดเจนในความหมายของมันจากการจัดกลุ่มความรู้ที่กว้างขวางใน P.SE และโดยการพูดว่า "รหัสการดีบักนั้นยากกว่าการเขียนสองเท่าดังนั้นถ้าคุณเขียนรหัสที่ฉลาดที่สุดที่คุณสามารถทำได้คุณ โดยนิยามแล้วมันไม่ฉลาดพอที่จะทำการดีบั๊ก "
Ampt

2
นั่นไม่ใช่ข้อโต้แย้งที่เหมาะสม ใช่แน่ใจว่า regex มีความซับซ้อน แต่ภาษาโปรแกรมอื่น ๆ Regex มีความซับซ้อนน้อยกว่าภาษาอื่น ๆ ส่วนใหญ่และเครื่องมือที่มีอยู่สำหรับ regex นั้นถูกแคระโดยเครื่องมือการพัฒนาสำหรับภาษาอื่น ๆ (FWIW ฉันทำงานอย่างกว้างขวางกับ regex และฉันไม่เคยใช้เครื่องมือเช่นนี้ ... ) มันเป็นความจริงง่ายๆที่แม้แต่ regex ที่ซับซ้อนก็ง่ายกว่าการแยกโค้ดที่ไม่ใช่ regex ที่เทียบเท่ากัน
Konrad Rudolph

@ KonradRudolph ฉันคิดว่าเรามีความขัดแย้งพื้นฐานเกี่ยวกับความหมายของคำว่าง่ายแล้ว ฉันจะให้คุณว่า regex สามารถเพิ่มเติมที่มีประสิทธิภาพหรือมากขึ้นที่มีประสิทธิภาพแต่ฉันไม่คิดว่าง่ายเป็นคำที่อยู่ในใจของทุกคนเมื่อคุณคิดว่า regex
Ampt

บางทีเราอาจทำ แต่คำจำกัดความของฉันคือการดำเนินการ: ฉันใช้ง่ายหมายถึงง่ายต่อการเข้าใจง่ายต่อการรักษาจำนวนข้อบกพร่องที่ซ่อนอยู่ต่ำ ฯลฯ แน่นอน regex ที่ซับซ้อนจะได้อย่างรวดเร็วก่อนดูไม่เข้าใจมาก แต่สิ่งเดียวกันนี้เป็นจริงสำหรับโค้ดที่ไม่ใช่ regex ที่เทียบเท่ากัน ฉันไม่เคยพูดว่า regex นั้นง่าย ฉันกำลังบอกว่ามันง่ายกว่า - ฉันกำลังเปรียบเทียบ นั่นเป็นสิ่งสำคัญ
Konrad Rudolph

7

ความหมายมีสองส่วน:

  • ก่อนอื่นคุณไม่ได้แก้ปัญหาเดิม
    นี่อาจหมายถึงความจริงที่ว่านิพจน์ทั่วไปมักเสนอวิธีแก้ไขปัญหาทั่วไปที่ ไม่สมบูรณ์
  • ประการที่สองตอนนี้คุณเพิ่มความยากลำบากเพิ่มเติมที่เกี่ยวข้องกับโซลูชันที่คุณเลือก
    ในกรณีของนิพจน์ทั่วไปความยากเพิ่มเติมอาจหมายถึงความซับซ้อนการบำรุงรักษาหรือความยากลำบากเพิ่มเติมที่เกี่ยวข้องกับการทำให้นิพจน์ทั่วไปเหมาะสมกับปัญหาที่ไม่ควรแก้ไข

7

ตามที่คุณถามในปี 2014 มันจะน่าสนใจที่จะมุ่งเน้นไปที่แนวคิดการเขียนโปรแกรมภาษาของบริบทปี 1997 เมื่อเทียบกับบริบทของวันนี้ ฉันจะไม่ป้อนการอภิปรายนี้ที่นี่ แต่ความคิดเห็นเกี่ยวกับ Perl และ Perl เองมีการเปลี่ยนแปลงอย่างมาก

แต่จะอยู่ในบริบทที่ 2013 ( de l'eau coulé sous les Ponts Depuis) ผมจะแนะนำให้ความสำคัญกับแนคท์ในเครื่องหมายคำพูดโดยใช้การ์ตูน XKCD ที่มีชื่อเสียงที่เป็นใบเสนอราคาโดยตรงของเจมี่ซาวินสกีหนึ่ง :

การ์ตูนจาก XKCD เกี่ยวกับ regexes, Perl และปัญหา

ก่อนอื่นฉันมีปัญหาในการเข้าใจการ์ตูนเรื่องนี้เพราะมันเป็นการอ้างอิงถึงคำพูดของ Zawinski และคำพูดของเนื้อเพลง Jay-z และการอ้างอิงของ GNU program --help -zflag 2ดังนั้นมันจึงเป็นวัฒนธรรมที่มากเกินไปสำหรับฉันที่จะเข้าใจ

ฉันรู้ว่ามันสนุกฉันรู้สึกมัน แต่ฉันไม่รู้จริงๆว่าทำไม คนมักจะทำเรื่องตลกเกี่ยวกับ Perl และ regexes โดยเฉพาะอย่างยิ่งตั้งแต่ยังไม่การเขียนโปรแกรมภาษา hipstiest ไม่ทราบจริงๆว่าทำไมมันควรจะเป็นความสนุกสนาน ... อาจจะเป็นเพราะแม่ค้า Perl ทำสิ่งโง่

ดังนั้นคำพูดเริ่มต้นดูเหมือนจะเป็นเรื่องตลกประชดประชันบนพื้นฐานของปัญหาในชีวิตจริง (ความเจ็บปวด?) ที่เกิดจากการเขียนโปรแกรมด้วยเครื่องมือที่เจ็บ เช่นเดียวกับค้อนสามารถทำร้ายช่างก่อสร้างได้การเขียนโปรแกรมด้วยเครื่องมือที่ไม่ใช่สิ่งที่นักพัฒนาซอฟต์แวร์จะเลือกถ้าเขาสามารถทำร้ายได้ (สมองความรู้สึก) บางครั้งการอภิปรายที่ดีเกี่ยวกับเครื่องมือที่ดีที่สุดที่เกิดขึ้น แต่ก็เกือบจะไร้ค่าสาเหตุมันเป็นปัญหาของรสนิยมของคุณหรือรสชาติทีมงานเขียนโปรแกรมของคุณ , วัฒนธรรมหรือเศรษฐกิจเหตุผล อีกหนึ่งการ์ตูน XKCD ที่ยอดเยี่ยมเกี่ยวกับเรื่องนี้:

การ์ตูนจาก XKCD เกี่ยวกับการอภิปรายการเขียนโปรแกรมเครื่องมือ

ฉันสามารถเข้าใจผู้คนที่รู้สึกเจ็บปวดเกี่ยวกับ regexes และพวกเขาเชื่อว่าเครื่องมืออื่นเหมาะกว่าสำหรับสิ่งที่ออกแบบมาสำหรับ regexes @ @ karl-bielefeldt ตอบคำถามของคุณด้วยการแสดงออกที่ดีมาพร้อมความรับผิดชอบที่ดีและ regexes มีความกังวลโดยเฉพาะอย่างยิ่งนี้ หากนักพัฒนาไม่สนใจว่า s-เขาจะจัดการกับ regexes ในที่สุดมันจะเป็นความเจ็บปวดสำหรับคนที่จะรักษารหัสในภายหลัง

ฉันจะจบด้วยคำตอบนี้เกี่ยวกับการยืนยันคำพูดโดยการเสนอราคาที่แสดงตัวอย่างทั่วไปจากDamian Conw ay Perl Best Practices (หนังสือปี 2005)

เขาอธิบายว่าการเขียนรูปแบบเช่นนี้:

m{'[^\\']*(?:\\.[^\\']*)*'}

... ไม่ยอมรับมากกว่าการเขียนโปรแกรมเช่นนี้ :

sub'x{local$_=pop;sub'_{$_>=$_[0
]?$_[1]:$"}_(1,'*')._(5,'-')._(4
,'*').$/._(6,'|').($_>9?'X':$_>8
?'/':$")._(8,'|').$/._(2,'*')._(
7,'-')._(3,'*').$/}print$/x($=).
x(10)x(++$x/10).x($x%10)while<>;

แต่มันสามารถเขียนใหม่ได้มันยังไม่สวย แต่อย่างน้อยตอนนี้ก็สามารถเอาตัวรอดได้

# Match a single-quoted string efficiently...
m{ '            # an opening single quote
    [^\\']*     # any non-special chars (i.e., not backslash or single quote)
    (?:         # then all of...`
    \\ .        # any explicitly backslashed char
    [^\\']*     #    followed by any non-special chars
    )*          # ...repeated zero or more times
    '           # a closing single quote
}x

รหัสรูปสี่เหลี่ยมชนิดนี้เป็นปัญหาที่สองที่ไม่ใช่ regexes ที่สามารถจัดรูปแบบได้อย่างชัดเจนบำรุงรักษาและอ่านได้


2
/* Multiply the first 10 values in an array by 2. */ for (int i = 0 /* the loop counter */; i < 10 /* continue while it is less than 10 */; ++i /* and increment it by 1 in each iteration */) { array[i] *= 2; /* double the i-th element in the array */ }
5gon12eder

6

หากมีสิ่งหนึ่งที่คุณควรเรียนรู้จากวิทยาการคอมพิวเตอร์มันเป็นลำดับชั้นของชัม ฉันจะบอกว่าปัญหาทั้งหมดเกี่ยวกับการแสดงออกปกติมาจากความพยายามในการแยกไวยากรณ์ที่ไม่มีบริบท เมื่อคุณสามารถกำหนดขีด จำกัด (หรือคิดว่าคุณสามารถกำหนดวงเงิน) เพื่อทำรังในระดับ CFG คุณจะได้รับนิพจน์ปกติที่ซับซ้อนและยาว


1
ใช่ ผู้ที่เรียนรู้นิพจน์ทั่วไปที่ไม่มีพื้นฐานของ CS นั้นมักไม่เข้าใจว่ามีบางสิ่งที่ regex ทางคณิตศาสตร์ไม่สามารถทำได้
benzado

5

นิพจน์ทั่วไปเหมาะสมกว่าสำหรับการทำโทเค็นมากกว่าการแยกวิเคราะห์แบบเต็ม

แต่สิ่งที่มีขนาดใหญ่อย่างน่าประหลาดใจที่โปรแกรมเมอร์ต้องแยกวิเคราะห์นั้นใช้ภาษาปกติ (หรือแย่กว่านั้นคือเกือบจะแยกเป็นภาษาปกติและถ้าคุณเขียนโค้ดเพิ่มอีกนิดหน่อย ... )

ดังนั้นหากมีตัวเคย "aha ฉันต้องเลือกข้อความแยกกันฉันจะใช้นิพจน์ปกติ" มันง่ายที่จะลงเส้นทางนั้นเมื่อคุณต้องการบางสิ่งที่ใกล้กับออโตเมติกแบบกดลง CFG parser หรือ ไวยากรณ์ที่มีประสิทธิภาพยิ่งขึ้น มันมักจะจบลงด้วยน้ำตา

ดังนั้นฉันคิดว่าการเสนอราคาไม่ใช่ regexps ที่กระแทกอย่างรุนแรงพวกเขามีการใช้งาน (และใช้งานได้ดีมีประโยชน์มากจริง ๆ ) แต่การพึ่งพา regexps มากเกินไป (หรือโดยเฉพาะอย่างยิ่งการเลือกที่ไม่สำคัญของพวกเขา) .


3

jwz เป็นเพียงแค่โยกออกของเขาด้วยคำพูดที่ การแสดงออกปกติไม่แตกต่างจากคุณสมบัติของภาษาใด ๆ - ง่ายต่อการทำให้ตกใจยากที่จะใช้อย่างหรูหรามีประสิทธิภาพในเวลาที่ไม่เหมาะสมในบางครั้งเอกสารที่ดีมักจะมีประโยชน์

อาจกล่าวได้ว่าเป็นเลขทศนิยม, การปิด, การวางแนววัตถุ, I / O แบบอะซิงโครนัสหรืออะไรก็ได้ที่คุณสามารถตั้งชื่อได้ หากคุณไม่รู้ว่ากำลังทำอะไรภาษาการเขียนโปรแกรมสามารถทำให้คุณเศร้า

หากคุณคิดว่า regexes อ่านยากลองอ่านการใช้ parser ที่เทียบเท่าเพื่อใช้รูปแบบที่เป็นปัญหา มักจะ regexes ชนะเพราะพวกเขามีขนาดกะทัดรัดกว่า parsers เต็ม ... และในภาษาส่วนใหญ่พวกเขาจะเร็วขึ้นเช่นกัน

ไม่ต้องออกจากการใช้การแสดงออกปกติ (หรือคุณสมบัติภาษาอื่น ๆ ) เพราะบล็อกเกอร์ที่ส่งเสริมตัวเองทำให้งบไม่เหมาะสม ลองด้วยตัวคุณเองและดูว่าอะไรเหมาะกับคุณ


1
FWIW เลขทศนิยมนั้นมีความยุ่งยากมากกว่า REs แต่ดูเหมือนง่ายกว่า ระวัง! (อย่างน้อย REs หากินมักจะดูอันตราย)
Donal Fellows

3

คำตอบเชิงลึกที่ชื่นชอบสำหรับเรื่องนี้ได้รับจาก Rob Pike ที่มีชื่อเสียงในโพสต์บล็อกทำซ้ำจากความคิดเห็นรหัสภายในของ Google: http://commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- and.html

สรุปก็คือไม่ใช่ว่าพวกเขาจะไม่ดีแต่พวกเขามักจะใช้สำหรับงานที่พวกเขาไม่เหมาะสมโดยเฉพาะอย่างยิ่งเมื่อมันมาถึง lexing และแยกวิเคราะห์บางส่วน

นิพจน์ทั่วไปนั้นยากที่จะเขียนยากที่จะเขียนได้ดีและอาจมีราคาแพงเมื่อเทียบกับเทคโนโลยีอื่น ๆ ... Lexers ในทางกลับกันค่อนข้างง่ายต่อการเขียนอย่างถูกต้อง (ถ้าไม่ใช่ขนาดกะทัดรัด) และง่ายต่อการทดสอบ ลองค้นหาตัวระบุและตัวเลข มันไม่ยากเกินไปที่จะเขียน regexp (บางอย่างเช่น "[a-ZA-Z _] [a-ZA-Z_0-9] *") แต่ก็ไม่ยากที่จะเขียนเป็นวงแบบง่ายๆ แม้ว่าประสิทธิภาพของลูปจะสูงขึ้นมากและจะเกี่ยวข้องกับรหัสน้อยลงภายใต้การครอบคลุม ห้องสมุดการแสดงออกปกติเป็นเรื่องใหญ่ การใช้ตัวระบุคำสั่งแยกวิเคราะห์ก็เหมือนกับการใช้ Ferrari เพื่อไปที่ร้านขายนม

เขาบอกว่ามากกว่านั้นการโต้แย้งว่านิพจน์ทั่วไปนั้นมีประโยชน์เช่นการจับคู่รูปแบบที่ใช้แล้วทิ้งในเครื่องมือแก้ไขข้อความ แต่ไม่ค่อยควรใช้ในโค้ดที่คอมไพล์แล้ว มันคุ้มค่าที่จะอ่าน


0

สิ่งนี้เกี่ยวข้องกับ epigram ของ Alan Perlis # 34:

สตริงเป็นโครงสร้างข้อมูลที่สมบูรณ์และทุกที่ที่ส่งผ่านจะมีการทำซ้ำมาก มันเป็นยานพาหนะที่สมบูรณ์แบบสำหรับการซ่อนข้อมูล

ดังนั้นหากคุณเลือกสตริงอักขระเป็นโครงสร้างข้อมูลของคุณ (และตามปกติโค้ดที่ใช้ regex เป็นอัลกอริทึมในการจัดการ) คุณมีปัญหาแม้ว่าจะใช้งานได้: การออกแบบที่ไม่ดีรอบ ๆ การแสดงข้อมูลที่ไม่เหมาะสม ขยายและไม่มีประสิทธิภาพ

อย่างไรก็ตามบ่อยครั้งมันใช้งานไม่ได้: ปัญหาดั้งเดิมไม่ได้รับการแก้ไขดังนั้นในกรณีนี้คุณมีปัญหาสองประการ


0

Regexes ใช้กันอย่างแพร่หลายสำหรับการแยกวิเคราะห์ข้อความที่รวดเร็วและสกปรก พวกเขาเป็นเครื่องมือที่ยอดเยี่ยมสำหรับแสดงรูปแบบที่ซับซ้อนเล็กน้อยกว่าเพียงแค่การจับคู่สตริงธรรมดา

อย่างไรก็ตามเมื่อ regexes พบปัญหาเซิร์ฟเวอร์ที่ซับซ้อนมากขึ้น

  1. ไวยากรณ์ของ regexes เหมาะสำหรับการจับคู่แบบเรียบง่ายตัวละครส่วนใหญ่จะจับคู่กัน นั่นเป็นสิ่งที่ยอดเยี่ยมสำหรับรูปแบบที่เรียบง่าย แต่เมื่อคุณจบลงด้วยการซ้อนซ้อนกันมากกว่าสองระดับคุณจะพบกับสิ่งที่ดูเหมือนเส้นเสียงรบกวนมากกว่ารหัสที่มีโครงสร้างที่ดี ฉันเดาว่าคุณสามารถเขียน regex เป็นชุดของสตริงที่ต่อกันพร้อมการเยื้องและความคิดเห็นในระหว่างเพื่อแสดงโครงสร้างของโค้ด แต่ดูเหมือนว่าจะไม่ค่อยเกิดขึ้นจริง
  2. การจับคู่ข้อความบางประเภทเท่านั้นที่เหมาะสมกับการ regexes บ่อยครั้งที่คุณพบว่าตัวเองได้รับตัวแยกวิเคราะห์ regex ที่รวดเร็วและสกปรกสำหรับภาษามาร์กอัปบางประเภทที่ใช้งานได้ แต่จากนั้นคุณพยายามครอบคลุมกรณีที่มีมุมมากขึ้นและคุณพบว่า regexes มีความซับซ้อนมากขึ้น
  3. ความซับซ้อนของเวลาของ regex อาจไม่ใช่แบบ obvoius มันไม่ได้เป็นเรื่องยากที่จะจบลงด้วยรูปแบบที่ใช้งานได้ดีเมื่อมันตรงแต่มี O (2 ^ n) ความซับซ้อนภายใต้บางกรณีที่ไม่ตรงกัน

ดังนั้นจึงเป็นเรื่องง่ายเกินไปที่จะเริ่มต้นด้วยปัญหาการประมวลผลข้อความใช้นิพจน์ทั่วไปกับมันและจบลงด้วยปัญหาสองปัญหาเดิมที่คุณพยายามแก้ไขและจัดการกับนิพจน์ทั่วไปที่พยายามแก้ไข (แต่การแก้ไม่ถูกต้อง) ปัญหาเดิม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.