Regex ao contrário - decompõe expressões regulares

16

O problema

Eu tenho várias expressões regulares que preciso usar em algum código, mas estou usando uma linguagem de programação que não suporta regex! Felizmente, eu sei que a sequência de teste terá um comprimento máximo e será composta apenas por ASCII imprimível.

O desafio

É necessário introduzir uma expressão regular e um número n, e saída de cada cadeia de caracteres composta por ASCII imprimível (códigos ASCII de 32 a 126, inclusive, para ~, sem separadores ou novas linhas) de comprimento inferior ou igual a nque corresponda a esse expressão regular. Você não pode usar expressões regulares incorporadas ou funções de correspondência de expressões regulares em seu código. Expressões regulares serão limitadas ao seguinte:

  • Caracteres literais (e escapes, que forçam um caractere a ser literal, assim \.é um literal ., \né um literal n(equivalente a apenas n) e \wsão equivalentes a w. Você não precisa oferecer suporte a seqüências de escape.)
  • . - curinga (qualquer caractere)
  • Classes de caracteres, [abc]significa "a ou b ou c" e [d-f]significam algo de d a f (então, d ou e ou f). Os únicos personagens que têm significado especial em uma classe de personagem são [e ](que sempre serão escapados, portanto, não se preocupe com eles), \(o caractere de escape, é claro), ^no início da classe de personagem (que é uma negação ) e -(que é um intervalo).
  • |- o operador OR, alternação. foo|barsignifica um fooou bar, e (ab|cd)ecorresponde a um abeou cde.
  • * - corresponda ao token anterior repetido zero ou mais vezes, ganancioso (tenta repetir o máximo de vezes possível)
  • + - repetido uma ou mais vezes, ganancioso
  • ? - zero ou uma vez
  • Agrupamento com parênteses, para os tokens de grupo para |, *. +ou?

A regex entrada será sempre válido (ou seja, você não tem que lidar com entrada, como ?abcou (fooou qualquer entrada inválida). Você pode produzir as strings na ordem que desejar, mas cada string deve aparecer apenas uma vez (não produza duplicatas).

Os casos de teste

Entrada: .*, 1
saída: (string vazia), , !, ", ..., },~

Entrada: w\w+, 3
saída: ww,www

Entrada: [abx-z][^ -}][\\], 3
saída: a~\, b~\, x~\, y~\,z~\

Entrada: ab*a|c[de]*, 3
saída: c, cd, ce, aa, cde, ced, cdd, cee,aba

Entrada: (foo)+(bar)?!?, 6
saída: foo, foo!, foofoo,foobar

Entrada: (a+|b*c)d, 4
saída: ad, cd, aad, bcd, aaad,bbcd

Entrada: p+cg, 4
saída: pcg,ppcg

Entrada: a{3}, 4
saída:a{3}

O vencedor

Isso é , então o código mais curto em bytes vencerá!

Maçaneta da porta
fonte
Temos permissão para suportar sequências de escape? Então isso é trivial.
John Dvorak
3
Sua explicação |faz muito pouco sentido. Parece não manipular grupos aninhados ou a|b|c. O que há de errado em usar as explicações padrão em termos de quão fortemente concatenação e alternância se ligam? (E você não tem desculpa para não usar a caixa de areia)
Peter Taylor
11
@PeterTaylor Na verdade, ele tem uma desculpa: meta.codegolf.stackexchange.com/q/1305/9498
Justin
2
A julgar pelo seu exemplo, o padrão deve corresponder a toda a cadeia? (Ao contrário de uma subsequência)
Martin Enders
3
@KyleKanos É uma pena que os problemas do mundo real não façam você pensar que deve aprender expressões regulares. : P Mas eles não são tão inacessível quanto parecem: regular-expressions.info/tutorial.html
Martin Ender

Respostas:

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

resultado:

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

Explicação: esta é uma implementação de regex de livros didáticos. R é o tipo de expressão regular, com os construtores A (alternativo), L (literal), T (então) e E (vazio / epsilon). A 'Estrela' usual não aparece porque eu a inline alternadamente durante a análise (consulte '%'). 'm' executa a simulação. O analisador (inicie em 'rs = ....') é apenas descida recursiva; 'k' analisa intervalos. A função '#' expande intervalos em alternações.

bazzargh
fonte
7

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

Essa resposta parece bastante longa para um código de golfe, mas há muitos operadores que precisam ser manipulados e eu sei qual é o objetivo de cada byte nesta resposta. Como não há resposta, eu a envio como um alvo para outros usuários vencerem. Veja se você pode fazer uma resposta mais curta :).

As duas funções principais são: fanalisa o regex começando no ith caractere e dgera as seqüências correspondentes, usando ros sub-regexes em que podemos recursar, 'a' a matriz que representa a parte do sub-regex atual ainda não processada, e um sufixo de sequência sque representa a parte da sequência gerada até agora.

Verifique também a saída da amostra e um chicote de teste .

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

Observe que as guias na solução original foram expandeditadas. Contar o número de caracteres no uso original unexpand < regex.py | wc.

gmatht
fonte
9
Eu nunca vi python parecer tão horrível.
user80551
Você não pode mudar def E(a,b):c=a[:];c.extend(b);return cpara E=lambda a,b:a[:].extend(b), idem paraA
user80551
Aparentemente não, pois .extend (b) não retorna nada.
precisa saber é o seguinte
11
Para o elif isinstance(e,str):, eu acredito que você poderia alterar o código interno para: 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)')(observe que #newlineé uma nova linha) (fonte: stackoverflow.com/a/103081/1896169 ) #
Justin Justin
11
Se você pudesse encontrar mais lugares para usar o truque exec, poderíamos facilmente alterar seu código em código ilegível :-)
Justin