ผมจะอธิบายนอก regex ส่วนหนึ่งของการทดสอบ primality ที่: regex ต่อไปนี้ได้รับString sซึ่งประกอบด้วยการทำซ้ำString t, tพบ
System.out.println(
"MamamiaMamamiaMamamia".replaceAll("^(.*)\\1+$", "$1")
); // prints "Mamamia"
วิธีการทำงานคือ regex จับ(.*)เข้า\1และดูว่ามี\1+ตามมาหรือไม่ การใช้^และ$ทำให้แน่ใจว่าการจับคู่ต้องเป็นสตริงทั้งหมด
ดังนั้นในทางหนึ่งเราได้รับString sซึ่งเป็น "หลาย" ของString tและ regex จะพบสิ่งนั้นt(ยาวที่สุดเท่าที่จะเป็นไปได้เนื่องจาก\1เป็นความโลภ)
เมื่อคุณเข้าใจแล้วว่าเหตุใด regex จึงทำงานได้ (ละเว้นทางเลือกแรกใน regex ของ OP ในตอนนี้) การอธิบายวิธีใช้สำหรับการทดสอบเบื้องต้นนั้นง่ายมาก
- ในการทดสอบความ
nเป็นอันดับแรกให้สร้างStringความยาวก่อนn(เติมด้วยค่าเดียวกันchar)
- นิพจน์ทั่วไปรวบรวม
Stringความยาวบางส่วน (พูดk) เข้ามา\1และพยายามจับคู่\1+กับส่วนที่เหลือของString
- หากมีการจับคู่แสดงว่า
nเป็นผลคูณที่เหมาะสมkดังนั้นจึงnไม่เป็นค่าเฉพาะ
- ถ้าไม่ตรงกันแสดงว่าไม่มีสิ่งนั้น
kที่หารnด้วยnเหตุนี้จึงเป็นไพรม์
วิธีการที่ไม่.?|(..+?)\1+ตรงกับตัวเลขที่สำคัญ?
จริงๆแล้วมันไม่! มันตรงกับ Stringมีความยาวไม่สำคัญ!
.?: ส่วนแรกของการจับคู่แบบสลับStringของความยาว0หรือ1(ไม่ใช่เฉพาะตามคำจำกัดความ)
(..+?)\1+: ส่วนที่สองของการสลับซึ่งเป็นรูปแบบของนิพจน์ทั่วไปที่อธิบายไว้ข้างต้นการจับคู่Stringของความยาวnที่เป็น "ตัวคูณ" Stringของความยาวk >= 2(กล่าวnคือเป็นส่วนประกอบไม่ใช่ไพรม์)
- โปรดทราบว่า
?จริง ๆ แล้วตัวปรับแต่งแบบไม่เต็มใจไม่จำเป็นสำหรับความถูกต้อง แต่อาจช่วยเร่งกระบวนการโดยพยายามให้เล็กลงkก่อน
สังเกตตัว! booleanดำเนินการส่วนเสริมในreturnคำสั่ง: มันลบล้างmatches. เมื่อ regex ไม่ตรงกันnเป็นสิ่งสำคัญ! มันเป็นตรรกะเชิงลบสองเท่าจึงไม่น่าแปลกใจที่มันสับสน !!
การทำให้เข้าใจง่าย
นี่คือการเขียนโค้ดใหม่ง่ายๆเพื่อให้อ่านง่ายขึ้น:
public static boolean isPrime(int n) {
String lengthN = new String(new char[n]);
boolean isNotPrimeN = lengthN.matches(".?|(..+?)\\1+");
return !isNotPrimeN;
}
ข้างต้นนั้นเหมือนกับโค้ด Java ดั้งเดิม แต่แยกออกเป็นหลายคำสั่งโดยมีการกำหนดตัวแปรโลคัลเพื่อให้เข้าใจตรรกะได้ง่ายขึ้น
นอกจากนี้เรายังสามารถทำให้ regex ง่ายขึ้นโดยใช้การทำซ้ำแบบ จำกัด ดังนี้:
boolean isNotPrimeN = lengthN.matches(".{0,1}|(.{2,})\\1+");
อีกครั้งที่ได้รับStringความยาวnเต็มไปด้วยเหมือนกันchar,
.{0,1}ตรวจสอบว่าn = 0,1ไม่ใช่เฉพาะ
(.{2,})\1+ตรวจสอบว่าnเป็นผลคูณที่เหมาะสมของk >= 2ไม่ใช่เฉพาะหรือไม่
ด้วยข้อยกเว้นของการปรับปรุงลังเล?บน\1(ละเว้นเพื่อความชัดเจน) ที่ regex ข้างต้นเป็นเหมือนเดิม
regex ที่สนุกยิ่งขึ้น
regex ต่อไปนี้ใช้เทคนิคที่คล้ายกัน ควรมีการศึกษา:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.+)(.+)(.+)=(\\1|\\2|\\3)+$", "$1! $2! $3!")
); // prints "Oh! My! God!"
ดูสิ่งนี้ด้วย
!new String(new char[n]).matches(".?|(..+?)\\1+")เทียบเท่ากับ!((new String(new char[n])).matches(".?|(..+?)\\1+")).