Regex ย้อนกลับ - สลายการแสดงออกปกติ


17

ปัญหา

ฉันมีสำนวนปกติที่ต้องใช้ในบางโค้ด แต่ฉันใช้ภาษาการเขียนโปรแกรมที่ไม่รองรับ regex! โชคดีที่ฉันรู้ว่าสตริงทดสอบจะมีความยาวสูงสุดและจะประกอบด้วย ASCII ที่พิมพ์ได้เท่านั้น

ความท้าทาย

คุณต้องป้อน regex และตัวเลขnและส่งออกทุกสตริงที่ประกอบด้วย ASCII ที่พิมพ์ได้ (รหัส ASCII 32 ถึง 126 รวมถึง~ไม่มีแท็บหรือบรรทัดใหม่) ที่มีความยาวน้อยกว่าหรือเท่ากับnที่ตรงกับ regex นั้น คุณไม่สามารถใช้นิพจน์ทั่วไปหรือฟังก์ชันจับคู่ regex ในรหัสของคุณได้เลย นิพจน์ทั่วไปจะถูก จำกัด ดังต่อไปนี้:

  • ตัวอักษรตัวอักษร (และหนีออกมาซึ่งบังคับให้ตัวละครที่จะเป็นตัวอักษรเพื่อให้\.เป็นตัวอักษร., \nเป็นตัวอักษรn(เทียบเท่าเพียงn) และ\wเทียบเท่ากับw. คุณไม่จำเป็นต้องลำดับการสนับสนุนการหลบหนี.)
  • . - อักขระตัวแทน (อักขระใด ๆ )
  • คลาสอักขระ[abc]หมายถึง "a หรือ b หรือ c" และ[d-f]หมายถึงอะไรตั้งแต่ d ถึง f (เช่น d หรือ e หรือ f) ตัวละครเดียวที่มีความหมายพิเศษในคลาสตัวละครคือ[และ](ซึ่งจะถูกหลบหนีอยู่เสมอดังนั้นไม่ต้องกังวลเกี่ยวกับสิ่งเหล่านั้น), \(ตัวหนีของหลักสูตร) ^ที่จุดเริ่มต้นของคลาสตัวละคร (ซึ่งเป็นการปฏิเสธ ) และ-(ซึ่งเป็นช่วง)
  • |- ตัวดำเนินการ OR การสลับ foo|barวิธีการอย่างใดอย่างหนึ่งfooหรือbarและ(ab|cd)eการแข่งขันอย่างใดอย่างหนึ่งหรือabecde
  • * - จับคู่โทเค็นก่อนหน้านี้ซ้ำเป็นศูนย์หรือมากกว่านั้นโลภ (พยายามทำซ้ำหลาย ๆ ครั้งเท่าที่จะทำได้)
  • + - ทำซ้ำอย่างน้อยหนึ่งครั้งโลภ
  • ? - ศูนย์หรือหนึ่งครั้ง
  • การจัดกลุ่มที่มีวงเล็บเพื่อราชสกุลกลุ่ม,| , หรือ*+?

อินพุต regex นั้นจะถูกต้องเสมอ (เช่นคุณไม่ต้องจัดการกับอินพุต?abcหรือ(fooหรืออินพุตที่ไม่ถูกต้อง) คุณสามารถส่งออกสตริงในลำดับใดก็ได้ที่คุณต้องการ แต่แต่ละสตริงจะต้องปรากฏเพียงครั้งเดียว (ไม่ส่งออกรายการที่ซ้ำกัน)

กรณีทดสอบ

การป้อนข้อมูล: .*, 1
เอาท์พุท: (สตริงว่าง) , !, ", ... },~

อินพุต: w\w+, 3
เอาต์พุต: ww,www

การป้อนข้อมูล: [abx-z][^ -}][\\], 3
เอาท์พุท: a~\, b~\, x~\, y~\,z~\

การป้อนข้อมูล: ab*a|c[de]*, 3
เอาท์พุท: c, cd, ce, aa, cde, ced, cdd, cee,aba

การป้อนข้อมูล: (foo)+(bar)?!?, 6
เอาท์พุท: foo, foo!, foofoo,foobar

การป้อนข้อมูล: (a+|b*c)d, 4
เอาท์พุท: ad, cd, aad, bcd, aaad,bbcd

อินพุต: p+cg, 4
เอาต์พุต: pcg,ppcg

อินพุต: a{3}, 4
เอาต์พุต:a{3}

ผู้ชนะ

นี่คือดังนั้นโค้ดที่สั้นที่สุดเป็นไบต์จะเป็นผู้ชนะ!


เราได้รับอนุญาตให้สนับสนุนลำดับการหลีกเลี่ยงหรือไม่? นี่เป็นเรื่องเล็กน้อย
John Dvorak

3
คำอธิบายของคุณ|สมเหตุสมผลน้อยมาก a|b|cมันดูเหมือนจะไม่จัดการกับกลุ่มที่ซ้อนกันหรือ มีอะไรผิดปกติกับการใช้คำอธิบายมาตรฐานในแง่ของวิธีการเรียงต่อกันอย่างมากและการผูกสลับ (และคุณไม่มีข้อแก้ตัวที่ไม่ได้ใช้กล่องทราย)
Peter Taylor

1
@PeterTaylor ที่จริงแล้วเขามีข้อแก้ตัว: meta.codegolf.stackexchange.com/q/1305/9498
Justin

2
ตัดสินโดยรูปแบบการสอบของคุณคุณต้องจับคู่สตริงทั้งหมดหรือไม่ (ตรงข้ามกับสตริงย่อย)
Martin Ender

3
@KyleKanos มันเป็นปัญหาที่น่าละอายในโลกแห่งความเป็นจริงไม่ได้ทำให้คุณคิดว่าคุณควรจะเรียนรู้การแสดงออกปกติ : P แต่พวกเขาไม่สามารถเข้าถึงได้เท่าที่ควร: regular-expressions.info/tutorial.html
Martin Ender

คำตอบ:


8

Haskell 757 705 700 692 679 667

import Data.List
data R=L Char|A R R|T R R|E
h=[' '..'~']
k(']':s)a=(a,s)
k('^':s)_=l$k[]s
k('-':c:s)(a:b)=k([a..c]++b)s
k('\\':c:s)a=k s$c:a
k(c:s)a=k s$c:a
l(a,b)=(h\\a,b)
c#E=L c
c#r=A(L c)r
o(a,b)=(foldr(#)E a,b)
t%0=E
t%n=A(t%(n-1))$T t$t%(n-1)
d s n=m(fst$r s)[[]] where{m E a=a;m(L c)a=[b++[c]|b<-a,length b<n];m(A r s)x=nub$(m r x)++m s x;m(T r s)a=m s$m r a;r s=w$e s E;w(u,'|':v)=(\(a,b)->(A u a,b))$r v;w x=x;e(')':xs)t=(t,xs);e s@('|':_)t=(t,s);e s@(c:_)t=g t$f$b s;e[]t=(t,[]);g t(u,v)=e v$T t u;f(t,'*':s)=(t%n,s);f(t,'+':s)=(T t$t%n,s);f(t,'?':s)=(A t E,s);f(t,s)=(t,s);b('(':s)=r s;b('\\':s:t)=(L s,t);b('.':s)=o(h,s);b('[':s)=o$k s[];b(s:t)=(L s,t)}

เอาท์พุท:

ghci> d ".*" 1
[""," ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_","`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~"]
ghci> d "w\\w+" 3
["ww","www"]
ghci> d "[abx-z][^ -}][\\\\]" 3
["x~\\","y~\\","z~\\","b~\\","a~\\"]
ghci> d "ab*a|c[de]*" 3
["aa","aba","c","ce","cd","cee","cde","ced","cdd"]
ghci> d "(foo)+(bar)?!?" 6
["foo!","foobar","foo","foofoo"]
ghci> d "(a+|b*c)d" 4
["ad","aad","aaad","cd","bcd","bbcd"]
ghci> d "p+cg" 4
["pcg","ppcg"]
ghci> d "a{3}" 4
["a{3}"]

คำอธิบาย: นี่คือการประยุกต์ใช้ตำราเรียน regex R คือประเภท regex โดยมี Constructor A (ตัวเลือก), L (ตัวอักษร), T (จากนั้น) และ E (ว่าง / epsilon) 'ดาว' ปกติไม่ปรากฏเนื่องจากฉันใส่เข้าไปเป็นทางเลือกระหว่างการแยกวิเคราะห์ (ดู '%') 'm' รันการจำลอง parser (เริ่มต้นที่ 'rs = .... ') เป็นเพียงการสืบเชื้อสายซ้ำ 'k' แยกวิเคราะห์ช่วง ฟังก์ชัน '#' ขยายช่วงเป็นทางเลือก


9

Python v2.7 1069 1036 950 925 897 884 871 833 822

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

ทั้งสองฟังก์ชั่นหลักคือการfแยกวิเคราะห์ regex เริ่มต้นที่iตัวละครที่ th และdที่สร้างสตริงการจับคู่โดยใช้rsub-regexes เราสามารถ recursed เป็น 'a' อาร์เรย์ที่เป็นตัวแทนของส่วนย่อย regex ปัจจุบันที่ยังไม่ได้ประมวลผล และส่วนต่อท้ายสตริงsซึ่งแทนส่วนของสตริงที่สร้างขึ้น

ยังตรวจสอบผลลัพธ์ตัวอย่างและเทียมทดสอบ

import sys;V=sys.argv;n=int(V[2]);r=V[1];S=len;R=range;C=R(32,127)
Z=[];z=-1;D='d(r,p,';F='for j in '
def f(i,a):
 if i>=S(r):return a,i
 c=r[i];x=0;I="|)]".find(c)
 if c in"([|":x,i=f(i+1,Z)
 if I+1:return([c,a,x],[a],[c,a])[I],i
 if'\\'==c:i+=1;x=c+r[i]
 return f(i+1,a+[x or c])
def d(r,a,s):
 if S(s)>n:return
 while a==Z:
        if r==Z:print s;return
        a=r[z];r=r[:z]
 e=a[z];p=a[0:z]
 if'|'==a[0]:d(r,a[1],s);d(r,a[2],s)
 elif']'==a[0]:
        g=a[1];N=g[0]=='^';g=(g,g[1:])[N];B=[0]*127;O=[ord(c[z])for c in g]
        for i in R(0,S(g)):
         if'-'==g[i]:exec F+'R(O[i-1],O[i+1]):B[j]=1'
         else:B[O[i]]=1
        for c in C:N^B[c]<1or d(r,Z,chr(c)+s)
 elif' '>e:d(r+[p],e,s)
 else:c=p[:z];exec{'.':F+'C:'+D+'chr(j)+s)','?':D+'s);d(r,p[:z],s)','*':F+'R(0,n+1):d(r,c,s);c+=[p[z]]','+':"d(r,p+['*',p[z]],s)"}.get(e,D+'e[z]+s)')
d(Z,f(0,Z)[0],"")

โปรดทราบว่าแท็บในการแก้ปัญหาเดิมที่ได้รับการexpandเอ็ด unexpand < regex.py | wcการนับจำนวนตัวอักษรในการใช้งานเดิม


9
ผมไม่เคยเห็นงูหลามดูว่าน่ากลัว
user80551

คุณเปลี่ยนไม่ได้def E(a,b):c=a[:];c.extend(b);return cเป็นE=lambda a,b:a[:].extend(b)เช่นนั้นสำหรับA
user80551

เห็นได้ชัดว่าไม่เป็น .extend (b) จะไม่ส่งคืนสิ่งใด
gmatht

1
สำหรับelif isinstance(e,str):ผมเชื่อว่าคุณสามารถเปลี่ยนรหัสภายในเป็น: exec{'.':'for c in C:d(r,p,s+chr(c))','?':'d(r,p,s);d(r,p[:z],s)','*':'''c=p[:z]#newline for i in R(0,n+1):d(r,c,s);c+=[p[z]]''','+':"d(r,p+['*',p[z]],s)",'\\':'d(r,p,e[1]+s)'}.get(e,'d(r,p,e+s)')(โปรดทราบว่า#newlineเป็นบรรทัดใหม่) (ที่มา: stackoverflow.com/a/103081/1896169 )
Justin

1
หากคุณสามารถหาสถานที่มากขึ้นในการใช้เคล็ดลับ exec เราได้อย่างง่ายดายสามารถเปลี่ยนรหัสของคุณเป็นรหัสที่อ่านไม่ได้ :-)
จัสติน

0

Prolog (SWI) , 590 ไบต์

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

R-L-S:-R*A,-(B,A,[]),setof(Z,(0/L/M,length(C,M),C+B+[],Z*C),S).
-R-->e+S,S-R.
R-T-->{R=T};"|",e+S,u+R+S-T.
Z+Y-->(".",{setof(C,32/126/C,R)};"[^",!,\E,"]",{setof(C,(32/126/C,\+C^E),R)};"[",\R,"]";"(",-R,")";{R=[C]},([C],{\+C^`\\.[|+?*(`};[92,C])),("*",{S=k(R)};"+",{S=c+R+k(R)};"?",{S=u+e+R};{S=R}),({Y=c+Z+S};c+Z+S+Y).
\C-->{C=[H|T]},+H,\T;{C=[]};+A,"-",+B,\T,{setof(C,A/B/C,H),append(H,T,C)}.
+C-->[92,C];[C],{\+C^`\\]-`}.
S+e+S.
[C|S]+D+S:-C^D.
S+(B+L+R)+T:-B=c,!,S+L+U,U+R+T;S+L+T;S+R+T.
S+k(K)+U:-S=U;S+K+T,S\=T,T+k(K)+U.
A/B/C:-between(A,B,C).
A^B:-member(A,B).
A*B:-string_codes(A,B).

เพรดิเคต-\3สามารถเรียกใช้ด้วยสองอาร์กิวเมนต์แรกที่สร้างอินสแตนซ์เพื่อสร้างสตริงที่จับคู่โดย regex

?- "[abx-z][^ -}][\\\\]"-3-S.
S = ["a~\\"] ;
S = ["b~\\"] ;
S = ["x~\\"] ;
S = ["y~\\"] ;
S = ["z~\\"] ;
false.

ลองออนไลน์!

รหัสที่ไม่ดี

generate_string(R, L, S) :-
    % parse regex
    string_codes(R, RC),
    regex_union(RE, RC, []),

    % bound string length
    between(0, L, M),
    length(SC, M),

    % find string
    match(SC, RE, []),

    string_codes(S, SC).

% Parsers
%%%%%%%%%  

regex_union(R) -->regex_concat(S), regex_union1(S, R).

regex_union1(R,T) --> [124], regex_concat(S), regex_union1(regex_union(R,S), T).
regex_union1(R, R) --> [].

regex_concat(R) --> regex_op(S), regex_concat1(S, R).

regex_concat1(R, T) --> regex_op(S), regex_concat1(regex_concat(R,S), T).
regex_concat1(R, R) --> [].

regex_op(regex_kleene(R)) --> regex_lit(R), [42].
regex_op(regex_concat(R,regex_kleene(R))) --> regex_lit(R), [43].
regex_op(regex_union(regex_empty,R)) --> regex_lit(R), [63].
regex_op(R) --> regex_lit(R).

regex_lit(regex_char([C])) --> [C], {\+ regex_ctrl(C)}.
regex_lit(regex_char([C])) --> [92], [C].

regex_lit(regex_char(CS)) --> [46], {findall(C, between(32,126, C), CS)}.

regex_lit(regex_char(DS)) --> 
    [91], [94], !, class_body(CS), [93],
    {findall(C, (between(32, 126, C), \+ member(C, CS)), DS)}.
regex_lit(regex_char(CS)) --> [91], class_body(CS), [93].

regex_lit(R) --> [40], regex_union(R), [41].

class_body([C|T]) --> class_lit(C),class_body(T).
class_body(CS) -->
    class_lit(C0), [45], class_lit(C1), class_body(T),
    {findall(C, between(C0, C1, C), H), append(H,T,CS)}.
class_body([]) --> [].

class_lit(C) --> [C], {\+ class_ctrl(C)}.
class_lit(C) --> [92], [C].

class_ctrl(C) :- string_codes("\\[]-", CS), member(C, CS).
regex_ctrl(C) :- string_codes("\\.[]|+?*()", CS), member(C, CS).

% Regex Engine
%%%%%%%%%%%%%% 

% The regex empty matches any string without consuming any characters.
match(S, regex_empty, S).

% A regex consisting of a single character matches any string starting with
% that character. The chanter is consumed.
match([C|S], regex_char(CS), S) :- member(C, CS).

% A union of two regex only needs to satisify one of the branches.
match(S, regex_union(L,R), T) :- match(S, L, T); match(S, R, T).     

% A concat of two regex must satisfy the left and then the right.
match(S, regex_concat(L, R), U) :- match(S, L, T), match(T, R, U).

% The kleene closure of a regex can match the regex 0 times or it can the regex
% once before matching the kleene closure again.
match(S, regex_kleene(_), S).
match(S, regex_kleene(K), U) :- match(S, K, T), S \= T, match(T, regex_kleene(K), U).
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.