Ordinamento parziale dei modelli Regex


25

Ai fini di questa sfida, diciamo che un modello regex corrisponde a una stringa se l' intera stringa è associata al modello, non solo a una sottostringa.

Dati due motivi regex  A  e  B , diciamo che  A  è più specializzato di  B   se ogni stringa che è abbinata a  A  è eguagliata anche da  B   ma non viceversa. Diciamo che  A  è equivalente a  B  se entrambi i modelli corrispondono esattamente allo stesso insieme di stringhe. Se nessuno dei due pattern è più specializzato dell'altro né sono equivalenti, diciamo che  A  e  B  sono incomparabili .

Ad esempio, il modello Hello, .*!è più specializzato di .*, .*!; i modelli (Hello|Goodbye), World!e Hello, World!|Goodbye, World!sono equivalenti; e i modelli Hello, .*!e.*, World! sono incomparabili.

La relazione "più specializzato di" definisce un rigoroso ordine parziale sull'insieme di schemi regex. In particolare, per tutti i modelli  A  e  B , è vera una delle seguenti condizioni:

  • A  è più specializzato di  B  ( A < B ).
  • B  è più specializzato di  A  ( A > B ).
  • A  e  B  sono equivalenti ( A = B ).
  • A  e  B  sono incomparabili ( AB ).

Quando  A  e  B  sono incomparabili, possiamo ulteriormente distinguere tra due casi:

  • A  e  B  sono disgiunti ( AB ), il che significa che nessuna stringa è abbinata da entrambi.
  • A  e  B  si intersecano ( AB ), il che significa che alcune stringhe sono abbinate da entrambi.

Sfida

Scrivi un programma o una funzione che accetta una coppia di schemi regex e li confronta usando l'ordine sopra. Cioè, se i due modelli sono  A  e  B , il programma dovrebbe determinare se  A < B ,  A > B ,
A = B  o  AB .

× Bonus del 92%  Viene dato un bonus aggiuntivo se, quando gli schemi sono incomparabili, il programma determina se si intersecano o si disgiungono.

Ingresso e uscita

Il programma dovrebbe accettare due pattern regex, come stringhe, con il sapore definito di seguito. È possibile leggere l'input tramite STDIN , la riga di comando , come argomenti di funzione o un metodo equivalente . Si può presumere che i modelli siano validi.

Il programma dovrebbe produrre esattamente uno dei quattro output distinti (o cinque output distinti se stai per ottenere il bonus sopra), a seconda del risultato del confronto (gli output esatti dipendono da te.) Puoi scrivere l'output su STDOUT , restituiscilo come risultato della funzione o usa un metodo equivalente .

Sapore di Regex

Puoi supportare qualunque funzionalità regex ti piaccia, ma devi supportare le seguenti:

  • Alternanza con |.
  • Quantificazione con *.
  • Raggruppamento con (e ).
  • Abbinare qualsiasi personaggio (possibilmente escludendo le nuove linee) con ..
  • (Opzionale: × 80% di bonus)  Abbinamento di classi di personaggi semplici e negate con […]e [^…], rispettivamente. Non è necessario supportare classi di caratteri predefinite (ad es. [:digit:]) Ma è necessario supportare intervalli di caratteri.
  • Personaggio in fuga con \. Dovrebbe almeno essere possibile escludere caratteri speciali (ad es. |*().[^-]\) E preferibilmente anche caratteri speciali comuni in altri gusti (ad es. {}), Ma il comportamento durante la fuga di caratteri non speciali non è specificato. In particolare, non è necessario supportare sequenze di escape speciali come \nper una nuova riga e simili. Una possibile implementazione è semplicemente quella di prendere il personaggio \come letterale.

Si può presumere l'esistenza di un carattere di input che non può essere confrontato da nessun letterale (cioè può solo essere abbinato a .classi di caratteri negate).

Regole aggiuntive

  • È possibile utilizzare le librerie regex o la funzionalità regex incorporata solo ai fini della corrispondenza e della sostituzione delle stringhe.
  • È possibile ignorare qualsiasi problema relativo alle impostazioni locali, come le regole di confronto.
  • Per affermare l'ovvio: il programma deve terminare. Dovrebbe essere eseguito in un ragionevole lasso di tempo, dati i modelli tipici (sicuramente non più di un'ora, preferibilmente molto meno).

punteggio

Questo è code-golf. Il tuo punteggio è il prodotto della dimensione del codice , in byte e uno qualsiasi dei bonus . Le punteggio più basso vince.

Casi test

Il formato dei casi di test è il seguente:

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

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

...

Dove si <Test ID>trova un identificatore per il caso di test, <Pattern A>e <Pattern B>sono i modelli regex ed <Ordering>è l'ordinamento tra di loro, ed è uno di:

  • <: <Pattern A>è più specializzato di <Pattern B>.
  • >: <Pattern B>è più specializzato di <Pattern A>.
  • =: I modelli sono equivalenti.
  • |: I modelli sono incomparabili e disgiunti.
  • X: I motivi sono incomparabili e intersecanti.

Il valore speciale <empty pattern>sta per il modello vuoto.

A. Schemi di base

B. Modelli complessi

C. Schemi di base con classi di caratteri

D. Schemi complessi con classi di caratteri

Programma di test

Il seguente frammento può essere utilizzato per confrontare i pattern 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
Wow. Qualsiasi risposta inviata a questo riceve un +1 automatico da parte mia. Determinare se due lingue formali sono isomorfe è abbastanza difficile. Determinare se uno è una sublanguage di un altro è un progetto CS completo universitario. @ ___ @
COTO

Esiste un comportamento specificato per i pattern regex non validi?
Paul Guyot,

@PaulGuyot No. Puoi presumere che i pattern siano validi.
Ell

1
Mi chiedo: hai scritto tu stesso vinto (per vedere quanto è fattibile per una domanda di codice golf) o no?
orgoglioso haskeller

1
@proudhaskeller l'ho fatto; Ho scritto lo snippet di prova. È una sfida difficile, e non ci sarà una linea qui, ma è golfabile.
Ell

Risposte:


10

Python 2, 522 byte * .92 = 480.24 537.28

Modifica 2 : -60 byte

Modifica : aggiunta spiegazione.

Definito è la funzione fche prende le due stringhe del modello come argomenti e restituisce '<', '=','>' , '|', o 'X'. Per elaborare tutti i casi di test è necessario meno di un minuto.

Il codice è costituito dai seguenti escape, ma con \r, \n \ted esadecimali (ma non \0) sostituiti con i loro valori di byte effettivi.

#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')

L'istruzione precedente fa eseguire il seguente codice:

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 il secondo esempio di codice è memorizzato nella stringa s, dall'espressione può essere prodotto qualcosa di simile al primo '#encoding=Latin\nexec"""%s"""'%__import__('zlib').compress(s). Potrebbe essere necessario correggere alcuni caratteri come byte nulli o barre rovesciate. Il codice decompresso è di circa 1000 800 byte, quindi forse è più offuscato rispetto al golf ... ma almeno sono riuscito a golfizzare un po 'la codifica: da Latin1aLatin .

Spiegazione

Il programma funziona usando gli indici della stringa come un modo grezzo per tenere traccia degli stati di analisi di una stringa. La nfunzione crea tabelle di transizione. Prima analizza la stringa e trova tutte le istanze di due tipi di transizioni. Innanzitutto, ci sono transizioni che non comportano l'aggiunta di un'altra lettera alla stringa. Ad esempio, saltando da *a all'inizio di un'espressione quantificata. In secondo luogo, ci sono transizioni per l'aggiunta di un personaggio, che semplicemente si sposta in avanti di un indice. Quindi viene calcolata la chiusura transitiva delle transizioni senza caratteri e queste vengono sostituite con le destinazioni delle transizioni a 1 carattere. Quindi restituisce una mappatura di un indice e un carattere a un insieme di indici.

La funzione principale esegue un BFS su possibili stringhe di input. Tiene traccia di un insieme di tutti gli indici possibili per qualsiasi frammento di una stringa che sta prendendo in considerazione. Ciò che ci interessa scoprire sono gli stati che sono accettati da entrambe le regex o dall'uno e non dall'altro. Per dimostrare che una regex è accettata, è necessario solo trovare un possibile percorso di transizione verso la fine del modello. Ma per dimostrare che uno è rifiutato, è necessario aver analizzato tutti i possibili percorsi. Pertanto, per determinare se le serie di stati possibili per il modello A e il modello B sono già coperte da quelle che sono state analizzate in precedenza, vengono registrate le coppie di un singolo stato in una regex e l'insieme di tutti i possibili stati nell'altro. Se non ce ne sono di nuovi, è OK tornare indietro. Certo, sarebbe anche possibile, e meno personaggi,


Molto bella! Supera tutti i test nei gruppi A e B (nessuna classe di caratteri, a quanto pare). Tuttavia, non riesco a far funzionare la versione compressa o ottenere lo stesso numero di byte. Ad ogni modo, puoi richiedere il x 0.92bonus quando calcoli il tuo punteggio. E, naturalmente, una spiegazione è benvenuta!
Ell

4

Haskell, 560 553 618

potrebbe ottenere alcuni dei bonus fatti in futuro.

questo non è ancora completamente golfato.

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)

una spiegazione ondulata dell'algoritmo:

reg!reg' restituisce il carattere richiesto, uno di "= <> x".

a#bè vero se non tutte le stringhe acorrispondenti sono uguali b.

c%regè un elenco di espressioni regolari tali che regcorrisponde a c:siff di una delle regexps nell'output corrisponde s. fondamentalmente corrisponde parzialmente al regex. tranne se lo cè '\0'. quindi impone regdi non ricevere più input, restituendo []se è regnecessario ottenere più input da abbinare e in [""]altro modo.

#funziona generando un elenco finito di tutti i possibili "stati regex" in cui si troveranno i due regexps dopo una stringa arbitraria. quindi per verificare se a<bcontrolliamo il tempo c'è uno "stato regex" in cui aè pienamente abbinato ma bnon è completamente abbinato.


Freddo! Ovviamente sei sulla strada giusta. Tuttavia, in questo momento fallisce il test B4.
Ell
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.