Regex (. NET รส), 182 181 145 132 126 114 104 100 98 97 96 ไบต์
การจดจำรูปแบบศิลปะ ASCII 2D? ดูเหมือนงาน regex! (ไม่เช่นนั้น)
ฉันรู้ว่าสิ่งนี้กำลังจะเริ่มการสนทนาที่ไม่มีที่สิ้นสุดอีกครั้งว่าการส่ง regex เป็นโปรแกรมที่ถูกต้องหรือไม่ แต่ฉันสงสัยว่ามันจะเอาชนะ APL หรือ CJam ต่อไปดังนั้นฉันจึงไม่เห็นอันตรายใด ๆ (ที่ถูกกล่าวว่าพวกเขาจะทำผ่านการทดสอบตายแข็งของเราสำหรับ"เป็นภาษาโปรแกรมอะไร?" .)
การดำเนินการนี้ใช้อินพุตเป็นสตริงที่จะจับคู่และผลลัพธ์คือจำนวนการจับคู่ที่พบ มันใช้_
แทน.
เพราะฉันต้องหลบหนีหลัง นอกจากนี้ยังต้องขึ้นบรรทัดใหม่ต่อท้าย
(X(X){1,21})(?=\D+((?>(?<-2>_)+)_))(?=.((?!\7)(.)*
.*(X\3X|()\1.)(?=(?<-5>.)*(?(5)!)
)){4,23}\7)
คุณสามารถทดสอบมันได้ที่RegexHeroหรือRegexStorm ) การแข่งขันจะเป็นแถวออบซิเดียนอันดับต้น ๆ ของพอร์ทัล หากคุณสามารถหากรณีทดสอบที่ล้มเหลวได้โปรดแจ้งให้เราทราบ!
คาถานี้คืออะไร?
คำอธิบายต่อไปนี้อนุมานความเข้าใจพื้นฐานของ .NET ของกลุ่มสมดุล แก่นสำคัญคือการจับภาพเป็นกองใน. NET regex - ทุกการจับภาพใหม่สำหรับชื่อเดียวกันจะถูกผลักลงบนสแต็ก แต่ยังมีไวยากรณ์ที่จะป๊อปอัพการจับจากกองเหล่านั้นอีกครั้ง ไปยังอีกในเวลาเดียวกัน สำหรับภาพที่สมบูรณ์มากขึ้นคุณสามารถดูคำตอบของฉันใน Stack Overflowซึ่งควรครอบคลุมรายละเอียดทั้งหมด
แนวคิดพื้นฐานคือการจับคู่รูปแบบที่ชอบ:
X{n}..{m}
X_{n}X.{m} |
X_{n}X.{m} | 3 to 22 times
X_{n}X.{m} |
X{n}..{m}
อยู่n
ระหว่าง 2 และ 22 (รวม) สิ่งที่ยุ่งยากคือทำให้ทุกอย่างn
และทุกอย่างm
เหมือนกัน เนื่องจากตัวละครที่เกิดขึ้นจริงจะไม่เหมือนกันเราจึงไม่สามารถใช้การอ้างอิงย้อนกลับได้
โปรดทราบว่า regex จะต้องฝังตัวขึ้นบรรทัดใหม่ซึ่งฉันจะเขียน\n
ดังต่อไปนี้
( # Open capturing group 1. This will contain the top of a portal, which
# I can reuse later to match the bottom (being of the same length).
X # Match a single X.
(X){1,21} # Match 1 to 21 X's, and push each separately on the <2> stack. Let's
# Call the number of X's captured N-1 (so N is the inner width of the
# portal).
) # End of group 1. This now contains N X's.
(?= # Start a lookahead. The purpose of this lookahead is to capture a
# string of N underscores in group 2, so I can easily use this to match
# the inside rows of the portal later on. I can be sure that such a
# string can always be found for a valid portal (since it cannot have 0
# inner height).
\D+ # Skip past a bunch of non-digits - i.e. *any* of the vaild characters
# of the input (_, X, \n). This to make sure I search for my N
# underscores anywhere in the remainder of the input.
( # Open capturing group 3. This will contain a portal row.
(?> # This is an atomic group. Once the engine hass successfully matched the
# contents of this group, it will not go back into the group and try to
# backtrack other possible matches for the subpattern.
(?<-2>_)+ # Match underscores while popping from the <2> stack. This will match as
# many underscores as possible (but not more than N-1).
) # End of the atomic group. There are two possible reasons for the
# subpattern stopping to match: either the <2> stack is empty, and we've
# matched N-1 underscores; or we've run out of underscores, in which
# case we don't know how many underscores we matched (which is not
# good).
_ # We simply try to match one more underscore. This ensures that we
# stopped because the <2> stack was empty and that group 3 will contain
# exactly N underscores.
) # End of group 3.
) # End of the lookahead. We've got what we want in group 2 now, but the
# regex engine's "cursor" is still at the end of the portal's top.
(?= # Start another lookahead. This ensures that there's actually a valid
# portal beneath the top. In theory, this doesn't need to be a
# lookahead - I could just match the entire portal (including the lines
# it covers). But matches cannot overlap, so if there were multiple
# portals next to each other, this wouldn't return all of them. By
# putting the remainder of the check in a lookahead the actual matches
# won't overlap (because the top cannot be shared by two portals).
. # Match either _ or X. This is the character above the portal side.
( # This group (4) is where the real magic happens. It's purpose is to to
# count the length of the rest of the current line. Then find a portal
# row in the next line, and ensure that it's the same distance from the
# end of the line. Rinse and repeat. The tricky thing is that this is a
# single loop which matches both inner portal rows, as well as the
# bottom, while making sure that the bottom pattern comes last.
(?!\7) # We didn't have a group 7 yet... group 7 is further down the pattern.
# It will capture an empty string once the bottom row has been matched.
# While the bottom row has not been matched, and nothing has been
# captured, the backreference will fail, so the negative lookahead will
# pass. But once we have found the bottom row, the backreference will
# always match (since it's just an empty string) and so the lookahead
# will fail. This means, we cannot repeat group 4 any more after the
# bottom has been matched.
(.)* # Match all characters until the end of the line, and push each onto
# stack <5>.
\n # Match a newline to go to the next line.
.* # Match as many characters as necessary to search for the next portal
# row. This conditions afterwards will ensure that this backtracks to
# the right position (if one exists).
( # This group (6) will match either an inner portal row, or the bottom
# of the portal.
X\3X # Match X, then N underscores, then X - a valid inner portal row.
| # OR
() # Capture an empty string into group 7 to prevent matching further rows.
\1. # Use the captured top to match the bottom and another character.
)
(?= # This lookahead makes sure that the row was found at the same
# horizontal position as the top, by checking that the remaining line
# is the same length.
(?<-5>.)* # Match characters while popping from the <5> stack.
(?(5)!)\n # Make sure we've hit end of the line, *and* the <5> stack is empty.
)
){4,23} # Repeat this 4 to 23 times, to ensure an admissible portal height.
# Note that this is one more than the allowed inner height, to account
# for the bottom row.
\7 # Now in the above repetition there is nothing requiring that we have
# actually matched any bottom row - it just ensured we didn't continue
# if we had found one. This backreference takes care of that. If no
# bottom row was found, nothing was captured into group 7 and this
# backreference fails. Otherwise, this backreference contains an empty
# string which always matches.
)
C #, 185 ไบต์
นี่คือฟังก์ชัน C # เต็มรูปแบบเพียงเพื่อให้รายการนี้ถูกต้อง ถึงเวลาที่ฉันเขียน "interpreter" บรรทัดคำสั่งสำหรับนิพจน์ปกติ. NET ...
static int f(string p){return System.Text.RegularExpressions.Regex.Matches(p,@"(X(X){1,21})(?=\D+((?>(?<-2>_)+)_))(?=.((?!\7)(.)*
.*(X\3X|()\1.)(?=(?<-5>.)*(?(5)!)
)){4,23}\7)").Count;}