Regex al contrario: decompone le espressioni regolari


16

Il problema

Ho un sacco di espressioni regolari che devo usare in alcuni codici, ma sto usando un linguaggio di programmazione che non supporta regex! Fortunatamente, so che la stringa di test avrà una lunghezza massima e sarà composta solo da ASCII stampabile.

La sfida

È necessario inserire una regex e un numero ne generare ogni stringa composta da ASCII stampabile (codici ASCII da 32 a 126 inclusi, a ~, senza tabulazioni o nuove righe) di lunghezza inferiore o uguale a nquella corrispondente a quella regex. Si può non utilizzare built-in espressioni regolari o funzioni corrispondenti regex nel codice a tutti. Le espressioni regolari saranno limitate alle seguenti:

  • Personaggi letterali (e escape, che costringono un personaggio ad essere letterale, quindi \.è letterale ., \nè un letterale n(equivalente a just n), ed \wè equivalente a w. Non è necessario supportare sequenze di escape.)
  • . - carattere jolly (qualsiasi carattere)
  • Classi di caratteri, [abc]significa "a o b o c" e [d-f]significa qualsiasi cosa da d a f (quindi, d o e o f). Gli unici personaggi che hanno un significato speciale in una classe di caratteri sono [e ](che saranno sempre sfuggiti, quindi non preoccuparti di quelli), \(il carattere di fuga, ovviamente), ^all'inizio della classe di caratteri (che è una negazione ) e -(che è un intervallo).
  • |- l'operatore OR, alternanza. foo|barsignifica o fooo bare (ab|cd)ecorrisponde a abeo cde.
  • * - abbina il token precedente ripetuto zero o più volte, avido (prova a ripetere il maggior numero di volte possibile)
  • + - ripetuto una o più volte, goloso
  • ? - zero o uno volte
  • Raggruppamento con parentesi, per i token di gruppo per |, *. +, o?

L'input regex sarà sempre valido (cioè, non è necessario gestire input simili ?abco (fooo qualsiasi input non valido). Puoi generare le stringhe nell'ordine che preferisci, ma ogni stringa deve apparire una sola volta (non generare duplicati).

I casi di test

Ingresso: .*, 1
Uscita: (stringa vuota), , !, ", ..., },~

Ingresso: w\w+, 3
uscita: ww,www

Ingresso: [abx-z][^ -}][\\], 3
uscita: a~\, b~\, x~\, y~\,z~\

Ingresso: ab*a|c[de]*, 3
uscita: c, cd, ce, aa, cde, ced, cdd, cee,aba

Ingresso: (foo)+(bar)?!?, 6
uscita: foo, foo!, foofoo,foobar

Ingresso: (a+|b*c)d, 4
uscita: ad, cd, aad, bcd, aaad,bbcd

Ingresso: p+cg, 4
uscita: pcg,ppcg

Ingresso: a{3}, 4
Uscita:a{3}

Il vincitore

Questo è , quindi vincerà il codice più breve in byte!


Siamo autorizzati a supportare sequenze di escape? Quindi questo è banale.
John Dvorak,

3
La tua spiegazione di |ha molto poco senso. Non sembra gestire gruppi nidificati o a|b|c. Cosa c'è di sbagliato nell'usare le spiegazioni standard in termini di quanto fortemente concatenazione e alternanza si legano? (E non hai scuse per non usare la sandbox)
Peter Taylor,

1
@PeterTaylor In realtà, ha una scusa: meta.codegolf.stackexchange.com/q/1305/9498
Justin

2
A giudicare dal tuo esempio, il modello deve corrispondere all'intera stringa? (A differenza di una sottostringa)
Martin Ender,

3
@KyleKanos È un peccato che i problemi del mondo reale non ti facciano pensare che dovresti imparare le espressioni regolari. : P Ma non sono così inaccessibili come potrebbero sembrare: regular-expressions.info/tutorial.html
Martin Ender

Risposte:


7

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)}

produzione:

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}"]

Spiegazione: questa è un'implementazione regex da manuale. R è il tipo regex, con i costruttori A (alternato), L (letterale), T (quindi) ed E (vuoto / epsilon). La solita "Stella" non appare perché la inserisco alternativamente durante l'analisi (vedi "%"). 'm' esegue la simulazione. Il parser (inizia da 'rs = ....') è solo una discesa ricorsiva; 'k' analizza gli intervalli. La funzione '#' espande gli intervalli in alternanze.


7

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

Questa risposta sembra piuttosto lunga per un codice golf, ma ci sono molti operatori che devono essere gestiti e so quale sia lo scopo di ogni byte in questa risposta. Dal momento che non esiste una risposta, la invio come target da battere per altri utenti. Vedi se riesci a fare una risposta più breve :).

Le due funzioni principali sono quelle fche analizzano la regex a partire dal ith carattere e dche generano le stringhe corrispondenti, usando ri sotto-regex in cui possiamo ricorrere, 'a' l'array che rappresenta la parte dell'attuale sub-regex non ancora elaborata, e un suffisso di stringa sche rappresenta la parte della stringa generata finora.

Controlla anche l' output del campione e un cablaggio di prova .

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],"")

Si noti che le schede nella soluzione originale sono state modificate expand. Per contare il numero di caratteri nell'uso originale unexpand < regex.py | wc.


9
Non ho mai visto un pitone così orribile.
user80551

Non puoi cambiarlo def E(a,b):c=a[:];c.extend(b);return cin E=lambda a,b:a[:].extend(b), idem perA
user80551

Apparentemente no, poiché .extend (b) non restituisce nulla.
Gmatht,

1
Per il elif isinstance(e,str):, credo che potresti cambiare il codice all'interno in: 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)')(nota che #newlineè una nuova riga) (fonte: stackoverflow.com/a/103081/1896169 )
Justin

1
Se potessi trovare altri posti dove usare il trucco di exec, potremmo facilmente cambiare il tuo codice in codice illeggibile :-)
Justin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.