ผมจะอธิบายนอก 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+"))
.