Partielle Anordnung von Regex-Mustern


25

Für diesen Zweck wird gesagt, dass ein reguläres Muster mit einer Zeichenfolge übereinstimmt, wenn die gesamte Zeichenfolge mit dem Muster übereinstimmt, nicht nur mit einer Teilzeichenfolge.

Gegeben seien zwei regex Muster  A  und  B , sagen wir , dass  A  ist mehr spezialisiert als  B ,   wenn jede Zeichenfolge, die durch abgestimmte  A  auch durch abgestimmte  B ,   aber nicht umgekehrt. Wir sagen , dass  A  ist äquivalent zu  B ,  wenn beide Muster entsprechen genau den gleichen Satz von Saiten. Wenn weder Muster mehr als die andere spezialisiert , noch sind sie gleichwertig, sagen wir , dass  A  und  B  sind unvergleichlich .

Zum Beispiel ist das Muster Hello, .*!spezialisierter als .*, .*!; die Muster (Hello|Goodbye), World!und Hello, World!|Goodbye, World!sind gleichwertig; und die Muster Hello, .*!und .*, World!sind unvergleichlich.

Die Beziehung "spezialisierter als" definiert eine strikte Teilreihenfolge auf der Menge der Regex-Muster. Insbesondere gilt für alle Muster  A  und  B genau eines der folgenden:

  • A  ist spezialisierter als  B  ( A < B ).
  • B  ist spezialisierter als  A  ( A > B ).
  • A  und  B  sind äquivalent ( A = B ).
  • A  und  B  sind unvergleichlich ( AB ).

Wenn  A  und  B nicht vergleichbar  sind, können wir weiter zwischen zwei Fällen unterscheiden:

  • A  und  B  sind disjunkt ( AB ), was bedeutet, dass beide Zeichenfolgen nicht übereinstimmen.
  • A  und  B  sind sich schneidende ( AB ), was bedeutet , dass einige Zeichenketten durch beide aufeinander abgestimmt sind.

Herausforderung

Schreiben Sie ein Programm oder eine Funktion , die ein Paar von Regex-Mustern verwendet und diese in der oben angegebenen Reihenfolge vergleicht. Das heißt, wenn die beiden Muster  A  und  B sind , sollte das Programm bestimmen, ob  A < B ,  A > B ,
A = B  oder  AB ist .

× 92% Bonus  Ein zusätzlicher Bonus wird gewährt, wenn das Programm bei unvergleichlichen Mustern feststellt, ob sie sich überschneiden oder nicht.

Ein- und Ausgang

Das Programm sollte zwei Regex-Muster als Zeichenfolgen in der unten definierten Variante akzeptieren. Sie können die Eingabe über STDIN , die Befehlszeile , als Funktionsargumente oder eine äquivalente Methode lesen . Sie können davon ausgehen, dass die Muster gültig sind.

Das Programm sollte abhängig vom Ergebnis des Vergleichs eine von genau vier verschiedenen Ausgaben erzeugen (oder fünf verschiedene Ausgaben, wenn Sie den obigen Bonus anstreben ) (die genauen Ausgaben liegen bei Ihnen.) Sie können die Ausgabe an STDOUT schreiben , geben Sie es als Ergebnis der Funktion zurück oder verwenden Sie eine äquivalente Methode .

Regex Aroma

Sie können beliebige reguläre Ausdrücke unterstützen, müssen jedoch die folgenden unterstützen:

  • Wechsel mit |.
  • Quantifizierung mit *.
  • Gruppieren mit (und ).
  • Passende jedes Zeichen (möglicherweise mit Ausnahme von Zeilenumbrüchen) mit ..
  • (Optional: × 80% Bonus)  Kombinieren Sie einfache und negierte Charakterklassen mit […]und [^…]. Sie müssen keine vordefinierten Zeichenklassen unterstützen (z. B. [:digit:]), aber Sie sollten Zeichenbereiche unterstützen.
  • Charakter Flucht mit \. Es sollte zumindest möglich sein, Sonderzeichen (dh |*().[^-]\) und vorzugsweise auch übliche Sonderzeichen in anderen Geschmacksrichtungen (zB {}) zu maskieren , aber das Verhalten beim Maskieren von Nicht-Sonderzeichen ist nicht spezifiziert. Insbesondere müssen Sie keine speziellen Escape-Sequenzen wie \nNewline und dergleichen unterstützen. Eine mögliche Implementierung besteht einfach darin, das Zeichen, das dem folgt, \als Literal zu verwenden.

Sie können davon ausgehen, dass es ein Eingabezeichen gibt, das von keinem Literal abgeglichen werden kann (dh, es kann nur von .und mit negierten Zeichenklassen abgeglichen werden ).

Zusätzliche Regeln

  • Sie dürfen Regex-Bibliotheken oder integrierte Regex-Funktionen nur zum Zwecke des Abgleichs und Ersetzens von Zeichenfolgen verwenden.
  • Sie können alle Probleme im Zusammenhang mit dem Gebietsschema ignorieren, z. B. Sortierregeln.
  • Um das Offensichtliche zu sagen: Ihr Programm muss beendet werden. Es sollte in angemessener Zeit ausgeführt werden, wenn typische Muster vorliegen (definitiv nicht mehr als eine Stunde, vorzugsweise viel weniger).

Wertung

Das ist Code-Golf. Ihr Ergebnis ist das Produkt der Codegröße in Byte und eine der Prämien . Die niedrigste Punktzahl gewinnt.

Testfälle

Das Format der Testfälle ist wie folgt:

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

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

...

Wo <Test ID>ist ein Bezeichner für den Testfall <Pattern A>und <Pattern B>sind die Regex-Muster und <Ordering>ist die Reihenfolge zwischen ihnen und ist eine der folgenden:

  • <: <Pattern A>ist spezialisierter als <Pattern B>.
  • >: <Pattern B>ist spezialisierter als <Pattern A>.
  • =: Die Muster sind gleichwertig.
  • |: Die Muster sind unvergleichlich und unzusammenhängend.
  • X: Die Muster sind unvergleichlich und überschneiden sich.

Der spezielle Wert <empty pattern>steht für das leere Muster.

A. Grundmuster

B. Komplexe Muster

C. Grundmuster mit Zeichenklassen

D. Komplexe Muster mit Zeichenklassen

Testprogramm

Das folgende Snippet kann zum Vergleichen von Regex-Mustern verwendet werden:

<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. Jede Antwort auf diese Frage wird von mir automatisch mit +1 bewertet. Es ist schwer genug festzustellen, ob zwei formale Sprachen isomorph sind. Die Bestimmung , ob ein a Subsprache eines anderen ist ein komplettes Bachelor - CS - Projekt. @ ___ @
COTO

Gibt es ein bestimmtes Verhalten für ungültige Regex-Muster?
Paul Guyot

@PaulGuyot Nein. Sie können davon ausgehen, dass die Muster gültig sind.
Ell

1
Ich frage mich - haben Sie selbst Won geschrieben (um zu sehen, wie machbar es für eine Code-Golf-Frage ist) oder nicht?
stolzer Haskeller

1
@ proudhaskeller habe ich getan; Ich habe das Test-Snippet geschrieben. Es ist eine schwierige Herausforderung, und es wird hier keine Einzeiler geben, aber es ist golffähig.
Ell

Antworten:


10

Python 2, 522 Bytes * .92 = 480.24 537.28

Edit 2 : -60 Bytes

Bearbeiten : Erklärung hinzugefügt.

Definiert ist , die Funktion , fdie die beiden Musterzeichenketten als Argumente und gibt nimmt '<', '=', '>', '|', oder 'X'. Die Bearbeitung aller Testfälle dauert weniger als eine Minute.

Der Code besteht aus Folgendem, jedoch mit \r, \n \tund hexadezimalem Escapezeichen (jedoch nicht \0), das durch die tatsächlichen Bytewerte ersetzt wird.

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

Die obige Anweisung bewirkt, dass der folgende Code ausgeführt wird:

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

Wenn das zweite Codebeispiel in der Zeichenfolge gespeichert ist s, kann durch den Ausdruck etwas Ähnliches wie das erste erzeugt werden '#encoding=Latin\nexec"""%s"""'%__import__('zlib').compress(s). Es kann notwendig sein, einige Zeichen wie Null-Bytes oder Backslashes zu korrigieren. Der entpackte Code ist fast 1000 800 Bytes, also ist er vielleicht mehr verschleiert als golfen ... aber zumindest habe ich es geschafft, die Codierung ein bisschen zu spielen: von Latin1bis Latin.

Erläuterung

Das Programm verwendet die Indizes des Strings als grobe Methode, um den Status des Parsens eines Strings zu verfolgen. Die nFunktion erstellt Übergangstabellen. Zuerst wird der String analysiert und es werden alle Instanzen von zwei Arten von Übergängen gefunden. Erstens gibt es Übergänge, bei denen der Zeichenfolge kein weiterer Buchstabe hinzugefügt wird. Beispiel: Springen von a *zum Anfang eines quantifizierten Ausdrucks. Zweitens gibt es Übergänge beim Hinzufügen eines Zeichens, die sich einfach um einen Index vorwärts bewegen. Dann wird das transitive Schließen der Übergänge ohne Zeichen berechnet, und diese werden für die Ziele der Übergänge mit 1 Zeichen eingesetzt. Es wird also eine Zuordnung eines Index und eines Zeichens zu einer Reihe von Indizes zurückgegeben.

Die Hauptfunktion führt ein BFS über mögliche Eingabezeichenfolgen aus. Es verfolgt einen Satz aller möglichen Indizes für jedes Fragment eines Strings, das es in Betracht zieht. Was uns interessiert, sind Zustände, die entweder von beiden Regexen oder vom einen und nicht vom anderen akzeptiert werden. Um zu zeigen, dass ein regulärer Ausdruck akzeptiert wird, muss nur ein möglicher Pfad für Übergänge zum Ende des Musters gefunden werden. Um jedoch zu zeigen, dass einer abgelehnt wird, müssen alle möglichen Pfade analysiert werden. Um zu bestimmen, ob die Sätze möglicher Zustände für Muster A und Muster B bereits durch diejenigen abgedeckt sind, die zuvor analysiert wurden, werden Paare eines einzelnen Zustands in einem regulären Ausdruck und der Satz aller möglichen Zustände im anderen aufgezeichnet. Wenn es keine neuen gibt, ist es in Ordnung, zurückzukehren. Natürlich wäre es auch möglich, und weniger Zeichen,


Sehr schön! Besteht alle Tests in den Gruppen A und B (anscheinend keine Zeichenklassen). Ich kann die komprimierte Version jedoch nicht zum Laufen bringen oder bekomme nicht die gleiche Byteanzahl. In beiden Fällen können Sie den x 0.92Bonus einfordern, wenn Sie Ihre Punktzahl berechnen. Und natürlich ist eine Erklärung willkommen!
Ell

4

Haskell, 560 553 618

könnte einige der Boni in der Zukunft erledigen.

Dies ist noch nicht vollständig golfen.

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)

Eine handgewellte Erklärung des Algorithmus:

reg!reg' Gibt das erforderliche Zeichen "= <> x" zurück.

a#bist wahr, wenn nicht jede Zeichenfolge, die apasst, auch mit übereinstimmt b.

c%regist eine Liste von regulären Ausdrücken, die regübereinstimmen, c:swenn einer der regulären Ausdrücke in der Ausgabe übereinstimmt s. Ich bin im Grunde teilweise der Regex entspricht. außer wenn cist '\0'. dann wird erzwungen reg, keine weiteren Eingaben zu erhalten, und es wird zurückgegeben, []wenn regweitere Eingaben für die Übereinstimmung erforderlich sind, und [""]andernfalls.

#erzeugt eine endliche Liste aller möglichen "regulären Ausdrücke", in denen sich die beiden regulären Ausdrücke nach einer beliebigen Zeichenfolge befinden. um dann zu überprüfen, ob a<bwir das Wetter überprüfen, gibt es einen "Regex-Zustand", in dem alles astimmt, aber bnicht alles stimmt.


Cool! Sie sind offensichtlich auf dem richtigen Weg. Im Moment schlägt der Test jedoch fehl B4.
Ell
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.