Ordenação parcial de padrões Regex


25

Para o objetivo desse desafio, dizemos que um padrão regex corresponde a uma sequência se toda a sequência for correspondida pelo padrão, não apenas uma substring.

Dado dois padrões de expressões regulares  A  e  B , dizemos que  A  é mais especializado que  B,   se cada string que corresponde a  A  também corresponde a  B,   mas não o contrário. Dizemos que  A  é equivalente a  B  se ambos os padrões corresponderem exatamente ao mesmo conjunto de cadeias. Se nenhum padrão é mais especializado que o outro, nem é equivalente, dizemos que  A  e  B  são incomparáveis .

Por exemplo, o padrão Hello, .*!é mais especializado que .*, .*!; os padrões (Hello|Goodbye), World!e Hello, World!|Goodbye, World!são equivalentes; e os padrões Hello, .*!e .*, World!são incomparáveis.

A relação "mais especializado que" define uma ordem parcial estrita no conjunto de padrões de expressões regulares. Em particular, para todos os padrões  A  e  B , exatamente um dos seguintes é verdadeiro:

  • A  é mais especializado que  B  ( A < B ).
  • B  é mais especializado que  A  ( A > B ).
  • A  e  B  são equivalentes ( A = B ).
  • A  e  B  são incomparáveis ​​( AB ).

Quando  A  e  B  são incomparáveis, podemos distinguir ainda mais entre dois casos:

  • A  e  B  são separados ( AB ), o que significa que nenhuma string é correspondida por ambos.
  • A  e  B  estão cruzando ( AB ), o que significa que algumas cadeias são correspondidas por ambas.

Desafio

Escreva um programa ou uma função que pegue um par de padrões regex e os compare usando a ordem acima. Ou seja, se os dois padrões são  A  e  B , o programa deve determinar se  A < B ,  A > B ,
A = B  ou  AB .

× 92% Bônus  Um bônus adicional é concedido se, quando os padrões forem incomparáveis, o programa determinar se eles estão se cruzando ou não.

Entrada e saída

O programa deve aceitar dois padrões de expressão regular, como seqüências de caracteres, no sabor definido abaixo. Você pode ler a entrada através do STDIN , a linha de comando , como argumentos de função ou um método equivalente . Você pode assumir que os padrões são válidos.

O programa deve produzir exatamente uma das quatro saídas distintas (ou cinco saídas distintas, se você optar pelo bônus acima), dependendo do resultado da comparação (as saídas exatas são com você.) Você pode gravar a saída em STDOUT , retorne-o como resultado da função ou use um método equivalente .

Sabor Regex

Você pode oferecer suporte a quaisquer recursos de regex que desejar, mas deve suportar os seguintes:

  • Alternância com |.
  • Quantificação com *.
  • Agrupando com (e ).
  • Combinando qualquer caractere (possivelmente excluindo novas linhas) com ..
  • (Opcional: × 80% de bônus)  Correspondência de classes de caracteres simples e negadas com […]e [^…], respectivamente. Você não precisa suportar nenhuma classe de caracteres predefinida (por exemplo [:digit:]), mas deve suportar intervalos de caracteres.
  • Personagem escapando com \. Deve ser pelo menos possível especificar caracteres especiais (por exemplo |*().[^-]\) e, de preferência, também caracteres especiais comuns em outros tipos (por exemplo {}), mas o comportamento ao escapar de caracteres não especiais não é especificado. Em particular, você não precisa oferecer suporte a seqüências de escape especiais, como \npara uma nova linha e similares. Uma implementação possível é simplesmente pegar o caractere após o \literal.

Você pode assumir a existência de um caractere de entrada que não pode ser correspondido por nenhum literal (ou seja, só pode ser correspondido por .classes de caracteres negadas.)

Regras adicionais

  • Você pode usar as bibliotecas de expressões regulares ou a funcionalidade interna de expressões regulares apenas para fins de correspondência e substituição de cadeias.
  • Você pode ignorar quaisquer problemas relacionados ao código do idioma, como regras de agrupamento.
  • Para afirmar o óbvio: seu programa deve terminar. Ele deve ser executado em uma quantidade razoável de tempo, considerando padrões típicos (definitivamente não mais que uma hora, de preferência muito menos).

Pontuação

Isso é código-golfe. Sua pontuação é o produto do tamanho do código , em bytes, e qualquer um dos bônus . A pontuação mais baixa vence.

Casos de teste

O formato dos casos de teste é o seguinte:

<Test ID>
<Pattern A>
<Ordering>
<Pattern B>

<Test ID>
<Pattern A>
<Ordering>
<Pattern B>

...

Onde <Test ID>é um identificador para o caso de teste <Pattern A>e <Pattern B>os padrões de regex e <Ordering>a ordem entre eles, e é um dos seguintes:

  • <: <Pattern A>é mais especializado que <Pattern B>.
  • >: <Pattern B>é mais especializado que <Pattern A>.
  • =: Os padrões são equivalentes.
  • |: Os padrões são incomparáveis ​​e disjuntos.
  • X: Os padrões são incomparáveis ​​e se cruzam.

O valor especial <empty pattern>representa o padrão vazio.

A. Padrões básicos

B. Padrões complexos

C. Padrões básicos com classes de caracteres

D. Padrões complexos com classes de caracteres

Programa de teste

O seguinte snippet pode ser usado para comparar padrões de regex:

<style>#main {display: none;}#main[loaded] {display: inline;}.pattern_container {position: relative;}.pattern_underlay, .pattern {font: 12pt courier, monospace;overflow: hidden;white-space: pre;padding: 7px;box-sizing: border-box;}.pattern_underlay {background-color: #dddddd;color: #707070;border-radius: 4px;box-shadow: 0.5px 0.5px 2.5px #aaaaaa;}.pattern_underlay[error] {background-color: #ffccbb;}.pattern {position: absolute;left: 0px;top: 0px;background: none;border: none;width: 100%;height: 100%;resize: none;white-space: normal;}#ordering {min-width: 28pt;text-align: center;font-size: 16pt;}#status {padding: 5px;background-color: #fffdce;box-shadow: 1.5px 1.5px 3.5px #aaaaaa;font-size: 10pt;white-space: pre;display: none;}#status[error] {display: inline;background-color: #ffe8df;}#status[loading] {display: inline;}.inline_code {background-color: #dddddd;font: 12pt courier, monospace;padding: 2px;}.placeholder {visibility: hidden;}.error_text {background-color: #fffcb7};</style><span id="main"><table><tr><td><div class="pattern_container"><div class="pattern_underlay" id="pattern1_underlay"></div><textarea class="pattern" id="pattern1" oninput="compare()">Hello, .*!</textarea></div></td><td id="ordering"></td><td><div class="pattern_container"><div class="pattern_underlay" id="pattern2_underlay"></div><textarea class="pattern" id="pattern2" oninput="compare()">.*, .*!</textarea></div></td></tr></table><br></span><span id="status" loading>Loading...</span><script type='text/javascript'>var Module = {setStatus: function (status) {document.getElementById("status").innerHTML = status;if (status == "") {compare();document.getElementById("status").removeAttribute("loading");document.getElementById("main").setAttribute("loaded", 1);}}};function underlay_chars(str) {if (/^\n*$/.exec(str))return str.split("\n").map(function () { return '<span class="placeholder"> \n</span>'; });if (str.indexOf("\n") >= 0)str = str.replace(/\s*$/gm, function (m) { return m.replace(/[^\n]/g, "\0"); });return (str + "\n").split("").map(function (c) {if (c == "\0") return "·";else return '<span class="placeholder">' + c + '</span>';});}function underlay_str(str) {return underlay_chars(str).join("");}function str_to_array32(str) {a = [];for (c of str) {n = c.charCodeAt(0);a.push(n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, n >> 24);}a.push(0, 0, 0, 0);return a;}function compare() {try {for (e of ["pattern1_underlay", "pattern2_underlay", "status"])document.getElementById(e).removeAttribute("error");for (e of ["pattern1", "pattern2"])document.getElementById(e + "_underlay").innerHTML = underlay_str(document.getElementById(e).value);c = Module.ccall("regex_compare", "number", ["array", "array"], [str_to_array32(document.getElementById("pattern1").value),str_to_array32(document.getElementById("pattern2").value)]);if (c >= 0)document.getElementById("ordering").innerHTML = "∥≬<>="[c];else {i = Module.ccall("regex_error_index", "number", [], []);l = Module.ccall("regex_error_length", "number", [], []);e = document.getElementById("pattern" + -c + "_underlay");t = underlay_chars(document.getElementById("pattern" + -c).value);e.setAttribute("error", 1);e.innerHTML =t.slice(0, i).join("") +'<span class="error_text">' + t.slice(i, i + l).join("") + "</span>" +t.slice(i + l).join("");e.setAttribute("error", 1);throw "Pattern error: " + Module.ccall("regex_error", "string", [], []).replace(/`(.*?)'/g, '<span class="inline_code">$1</span>');}} catch (e) {document.getElementById("ordering").innerHTML = "??";document.getElementById("status").innerHTML = e;document.getElementById("status").setAttribute("error", 1);}}</script><script async type="text/javascript" src="https://gist.githack.com/anonymous/91f27d6746566c7b4e4c/raw/c563bf84a01c3a1c6e5f021369a3e730a2e74a1a/rpo.js"></script>


10
Uau. Qualquer resposta submetida a isso recebe um +1 automático de mim. Determinar se duas linguagens formais são isomórficas já é bastante difícil. Determinar se um é uma sub - linguagem do outro é um projeto completo de graduação em CS. @ ___ @
COTO

Existe algum comportamento especificado para padrões de regex inválidos?
Paul Guyot

@PaulGuyot Não. Você pode assumir que os padrões são válidos.
`` #

1
Eu me pergunto - você escreveu você mesmo (para ver como é viável para uma questão de código de golfe) ou não?
haskeller orgulhoso

1
@proudhaskeller eu fiz; Eu escrevi o trecho de teste. É um desafio difícil, e não haverá nenhuma linha aqui, mas é jogável.
Ell

Respostas:


10

Python 2, 522 bytes * .92 = 480,24 537,28

Editar 2 : -60 bytes

Edit : Adicionado explicação.

Definida é a função fque leva as duas cadeias padrão como argumentos e retorna '<', '=', '>', '|', ou 'X'. É necessário menos de um minuto para processar todos os casos de teste.

O código consiste no seguinte, mas com \r, \n \te hex e escapes (mas não \0) substituídos por seus valores reais de bytes.

#encoding=Latin
exec"""x\xda]RMo\xdb0\x0c\xbd\xe7Wx\'K\x96\x92\xc5mOR\xb8\xdf1@%|\x98%X\x80a\x19\x96\x02\x03n\xf2\xdfG:i;\xec$\x92z|\x8f_\x1b\x84%m~\xca\xbe\x1c\x0e\xbd\x0fU\x10Agi\x0e\x87\xea\n\x1f\xf9n{=\xea\0\x93\x08\xd2\xaez\xd0\x99\xcc,m\x07g\xbb\x80s\x9b\x08\xee\x8cRo"\xf3\x8bHy!-\x95\xd7\xa9\x8aS\xb50O5\xc3&\xb68\x0b\xe7\xb1\x19t\x92\x8a\x1d\xaf]\xc2f\x94\x92\x111T\xf3\xf1j\xba\x1b\x081r\xa2\x97\xea\xa5\x11\x03\x9bI\xca\xe6\xed\xe7\xab\xbd\xde`\xb6\x8b"\xd1\xc5\xf7\xd7?^l\xa7\xaeKK\xd7i\x91\x92\x8b\xaaE\x16\x8e\x9c\x12#3\x86\xe0"\xc6\xc9\x15\xfd\x86\xae\\\xde\xcc^\xa7\x94;,\xea\x94t\x08\x84\xa6J\x82\xee%\xb1\xe8\xacW\xb9\xb3\x14f\xd9\x84\xeb\x89\xe1\x8b\xd5\xa3r\xeb\xbf\x81D\rS\xf5\x8b/\xd7e\xaao\xf0\xeb\xf2\xbbv\xdd\xf1\x15\x1f\x93\xe4Aq\xff\x19\xc6\x98\x8b\xa8E\xad\xb2\xaae-m\x843\xc5\xd7!\x8e\xbe\xca.\x1a4\x01\xe8E;@-\xe4\xad9\xd5\xa7\x10\xa7\x9eg\xcea\x10\x83\x0e\xd2\r\x973\xb2o\xb8\xd7\x06\xc2\x0f\xa8\xdf\xdfk\x1b\x15\xb4v\x84H\xc9\xad]\xc1\x83C;\x03m\xc3\x16p\x1f\xe3\x1d\xbf\xa4\xe2\xbe\x8d\x1eX)\x1e\t\x9dv\xf3\xa9\xcd\xe8xGU\x9e\x0b\t\x97\xd6\x0c\x8c\xf2\nxa\xa9\x19u\xaf\xf2iN3\r\xd1\xae\x0f\xe3\x13\x0c@h\xb5W\xb0\xaad3\xef\t\x91s]R=~\xc3^Lv\xc7\x16\x15\xf4\xfb\xa7\x88ze_~B\x06\x80\x99\x03\x86\x7f\x0bY\x06U\xd2.\xeaV\x95\x87$\xd1\xce\xff\x8b\xbf\x9a\x99\xe0\x03u\xa1 =o0<n\xd0\xef]s`b\xb7\x98\x89\xael\xd2\x85\xceO:>\x80j\xfa\xdeb\x95\x95k\x91N\xbe\xfc'5\xac\xe7\xe8\x15""".decode('zip')

A instrução acima faz com que o seguinte código seja executado:

z=frozenset
def f(f,s):
 u={s};d,l,f=n(f);w,h,s=n(s);_=0;r=[[z(f[0]),z(s[0])]]
 for e,o in r:
  p=z(zip([e]*h,o)+zip(e,[o]*l))
  if p-u:_|=((l in e)+2*(h in o))*4/3;u|=p;r+=[[reduce(z.__or__,(oo[i+1]for i in ii if ff[i]in[t,4][t<4:]),z())for ii,oo,ff in(e,f,d),(o,s,w)]for t in z([d[i]for i in e]+[w[i]for i in o])]
 return'|=><X'[_-3]
def n(s):
 s=list('('+s+')');i=0
 while s[i:]:f=s[i];h='()|*.'.find(f);s[i]=(h,f)[h<0];s[i:i+1]*=f!='\\';i+=1;l=i;h=1;w=e=[];p=[0];t=[{l}]
 while i:
  d=[i];i-=1;o=[i];f=s[i];t=[{i}]+t
  if f<1:h-=1;e+=zip(o*l,d+s.pop());w.pop()
  if f==1:h+=1;w=w+o;s+=[[]];e+=[o+d]
  if f==2:s[-1]+=d;e+=[(i,w[-1])]
  if h==p[-1]:e+=[t[-1:]+o,[i,1+t.pop()]];p.pop()
  if f==3:p+=[h];t+=o
 for f,o in e:
  for n in t:n|=(n,t[o])[f in n]
 return s+[1],l,t

Se o segundo exemplo de código estiver armazenado na cadeia s, algo semelhante ao primeiro poderá ser produzido pela expressão '#encoding=Latin\nexec"""%s"""'%__import__('zlib').compress(s). Pode ser necessário corrigir alguns caracteres, como bytes nulos ou barras invertidas. O código descompactado tem quase 1000 800 bytes, então talvez seja mais ofuscado do que o golfe ... mas pelo menos eu consegui jogar um pouco a codificação: de Latin1para Latin.

Explicação

O programa funciona usando os índices da string como uma maneira simples de acompanhar os estados de análise de uma string. A nfunção cria tabelas de transição. Primeiro, ele analisa a cadeia e encontra todas as instâncias de dois tipos de transições. Primeiro, há transições que não envolvem adicionar outra letra à string. Por exemplo, saltando de a *para o início de uma expressão quantificada. Em segundo lugar, há transições de adição de um caractere, que simplesmente avançam em um índice. Em seguida, o fechamento transitivo das transições sem caracteres é calculado e esses são substituídos pelos destinos das transições com 1 caractere. Portanto, ele retorna um mapeamento de um índice e um caractere para um conjunto de índices.

A função principal executa um BFS sobre possíveis sequências de entrada. Ele rastreia um conjunto de todos os índices possíveis para qualquer fragmento de uma string que esteja considerando. O que estamos interessados ​​em encontrar são estados que sejam aceitos por ambas as regexes ou por um e não pelo outro. Para mostrar que uma regex é aceita, é necessário apenas encontrar um caminho possível de transições até o final do padrão. Mas para mostrar que alguém é rejeitado, é necessário ter analisado todos os caminhos possíveis. Portanto, para determinar se os conjuntos de estados possíveis para o padrão A e o padrão B já estão cobertos pelos que foram analisados ​​anteriormente, os pares de um único estado em uma regex e o conjunto de todos os estados possíveis na outra são registrados. Se não houver novos, não há problema em voltar. Claro, também seria possível, e menos caracteres,


Muito agradável! Passa em todos os testes nos grupos A e B (sem classes de caracteres, ao que parece.) Mas não consigo fazer a versão compactada funcionar ou obter a mesma contagem de bytes. De qualquer forma, você pode reivindicar o x 0.92bônus ao calcular sua pontuação. E, claro, uma explicação é bem-vinda!
Ell

4

Haskell, 560 553 618

pode receber alguns dos bônus realizados no futuro.

isso ainda não está totalmente jogado.

import Data.List
c%j|'\\':h:s<-j=[s|c==h]|(a,b):_<-[(a,b)|x<-[0..length j],(a,'|':b)<-[splitAt x j],snd(k b)==[]]=c%a++c%b|'(':s<-j,(a,_:'*':b)<-k s=map(++j)(c%a)++c%b|'(':s<-j,(a,_:b)<-k s=map(++b)(c%a)|h:'*':s<-j=map(++j)(c%[h])++c%s
c%"."=[""|c>'\0']
c%s@[_]=c%('\\':s)
c%(a:b)=map(++b)(c%[a])
c%s=[""|c>'\0']
a&b=nub[(x,nub$b>>=(c%))|c<-[' '..'~'],x<-c%a]
g e(k@(a,l):r)|j<-a&l\\e=g(k:e)(j++r)
g e[]=e
a#b=or[all(null.('\0'%))m|(x,m)<-g[][(a,[b])],""<-'\0'%x]
a!b|a#b,b#a='x'|a#b='>'|b#a='<'|0<1='='
k"("=("","(")
k(c:s)|'('<-c,(x,y)<-k$tail b=('(':a++')':x,y)|')'<-c=("",')':s)|0<1=(c:a,b)where(a,b)=k s
k j=(j,j)

uma explicação ondulada do algoritmo:

reg!reg' retorna o caractere necessário, um de "= <> x".

a#bé verdadeiro se nem todas as seqüências acorrespondentes também forem correspondidas b.

c%regé uma lista de expressões regulares que regcorrespondem c:sse um dos regexps na saída corresponder s. i é basicamente corresponde parcialmente ao regex. exceto se cfor '\0'. então força rega não receber mais informações, retornando []se regdeve obter mais informações para corresponder ou [""]não.

#funciona gerando uma lista finita de todos os possíveis "regex-state" em que os dois regexps estarão após uma sequência arbitrária. para verificar se a<bverificamos o clima, existe um "estado regular" no qual aé totalmente correspondido, mas bnão totalmente.


Legal! Você está obviamente no caminho certo. No entanto, no momento, ele falha no teste B4.
Ell
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.