Часткове впорядкування моделей Regex


25

Для цієї задачі ми говоримо, що шаблон регулярного вираження відповідає рядку, якщо весь рядок узгоджується з шаблоном, а не лише підрядком.

Дано два регулярних виразів моделі   і  B , ми говоримо , що   є більш спеціалізовані , ніж  B ,   якщо кожен рядок, підібраний  А  також відповідає за  B ,   але не навпаки. Ми говоримо , що   є еквівалентом для  B ,  якщо обидві моделі відповідають точно такий же набір рядків. Якщо жодна закономірність не є більш спеціалізованою, ніж інша, або не є рівнозначною, ми говоримо, що  A  і  B  є незрівнянними .

Наприклад, модель Hello, .*!є більш спеціалізованою, ніж .*, .*!; закономірності (Hello|Goodbye), World!та Hello, World!|Goodbye, World!рівнозначні; і закономірності Hello, .*!і .*, World!незрівнянні.

Співвідношення "більш спеціалізоване, ніж" визначає суворий частковий порядок набір шаблонів регулярних виразів. Зокрема, для всіх шаблонів  A  і  B справедливо одне з наступних:

  • A  більш спеціалізований, ніж  B  ( A < B ).
  • B  більш спеціалізований, ніж  A  ( A > B ).
  • A  і  B  еквівалентні ( A = B ).
  • A  і  B  незрівнянні ( AB ).

Коли  A  і  B  є непорівнянними, ми можемо додатково розрізняти два випадки:

  • A  і  B  - неперервні ( AB ), це означає, що обидві строки не узгоджуються.
  •  І  B  є пересічні ( AB ), що означає , що деякі рядки збігаються з обома.

Виклик

Напишіть програму або функцію, яка бере пару зразків регулярних виразів і порівнює їх, використовуючи вищезазначений порядок. Тобто, якщо два візерунки   і  В , програма повинна визначити , є чи  A < B ,  A > B , A = B  або  AB .

× 92% бонус  Додатковий бонус надається, якщо програма, коли шаблони є непорівнянними, програма визначає, перетинаються вони чи не перетинаються.

Вхід і вихід

Програма повинна прийняти два шаблони регулярного вираження, як рядки, в ароматі, визначеному нижче. Ви можете читати вхід через команду STDIN , командний рядок , як аргументи функції або еквівалентний метод . Ви можете припустити, що шаблони дійсні.

Програма повинна створити один із чотирьох рівномірних результатів (або п’ять різних результатів, якщо ви збираєтесь отримати вищевказаний бонус), залежно від результату порівняння (точні результати залежить від вас.) Ви можете записати вихід у STDOUT , поверніть його як результат функції або використовуйте еквівалентний метод .

Regex Flavour

Ви можете підтримувати всі функції, які вам подобаються, але ви повинні підтримувати наступні:

  • Чергування з |.
  • Кількісне визначення з *.
  • Групування з (і ).
  • Узгодження будь-якого символу (можливо, виключаючи нові рядки) із ..
  • (Необов’язково: × 80% бонус)  Збірка простих та заперечених класів символів відповідно […]та [^…]відповідно. Вам не потрібно підтримувати будь-які заздалегідь задані класи символів (наприклад [:digit:]), але ви повинні підтримувати діапазони символів.
  • Персонаж рятується с \. Потрібно, принаймні, мати можливість схибити спеціальних символів (тобто |*().[^-]\), а також бажано також використовувати звичайних спеціальних символів в інших ароматах (наприклад {}), але поведінка під час втечі неспеціальних символів не визначена. Зокрема, вам не доведеться підтримувати спеціальні послідовності евакуації, такі як \nдля нового рядка тощо. Можлива реалізація полягає в тому, щоб просто прийняти символ, що слідує за \буквальним, буквально.

Ви можете припустити існування вхідного символу, який не може бути узгоджений жодним літералом (тобто він може відповідати лише .класам символів і заперечувати їх.)

Додаткові правила

  • Ви можете використовувати бібліотеки regex або вбудовану функцію регулярного вибору лише для відповідності рядків і їх заміни.
  • Ви можете ігнорувати будь-які проблеми, пов’язані з місцевою локацією, наприклад правила зіставлення.
  • Затверджуючи очевидне: ваша програма повинна припинитись. Він повинен виконуватись за розумну кількість часу за заданими типовими зразками (безумовно, не більше години, бажано набагато менше.)

Оцінка балів

Це код-гольф. Ваша оцінка є продуктом з розміру коду , в байтах, і будь-який з бонусів . Виграє найнижчий бал .

Випробування

Формат тестів такий:

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

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

...

Де <Test ID>є ідентифікатором для тестового випадку, <Pattern A>і <Pattern B>є зразки регулярного вираження та <Ordering>впорядкування між ними, і є одним із:

  • <: <Pattern A>є більш спеціалізованим, ніж <Pattern B>.
  • >: <Pattern B>є більш спеціалізованим, ніж <Pattern A>.
  • =: Шаблони рівноцінні.
  • |: Шаблони незрівнянні та непересічні.
  • X: Шаблони незрівнянні та перетинаються.

Особливе значення <empty pattern>означає порожній візерунок.

А. Основні закономірності

В. Складні візерунки

C. Основні зразки з класами символів

D. Складні візерунки з класами символів

Тестова програма

Наступний фрагмент може бути використаний для порівняння зразків регулярних виразів:

<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
Ого. Будь-яка відповідь, надіслана на це, отримує від мене автоматичний +1. Визначити, чи є дві формальні мови ізоморфними досить важко. Визначення того, чи є один підмовою іншого, є повним студентським проектом CS. @ ___ @
COTO

Чи є визначена поведінка для недійсних зразків регулярних виразів?
Пол Гійот

@PaulGuyot Ні. Ви можете вважати, що шаблони є дійсними.
Ell

1
Цікаво - ви писали, що виграли самі (щоб побачити, наскільки це можливо для кодового питання про гольф) чи не ви?
гордий haskeller

1
@proudhaskeller я зробив; Я написав фрагмент тесту. Це складний виклик, і тут не буде жодного однопланового вкладиша, але це пограбано.
Ell

Відповіді:


10

Python 2, 522 байт * .92 = 480,24 537,28

Редагувати 2 : -60 байт

Редагувати : Додано пояснення.

Визначається функція , fяка приймає два паттерна рядки в якості аргументів і повертає '<', '=', '>', '|', або 'X'. На обробку всіх тестових випадків потрібно менше однієї хвилини.

Код складається з наступних, але з \r, \n \tі шістнадцяткові втечі (але не \0) замінені на їхні фактичні значення байтів.

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

Наведене вище твердження призводить до виконання наступного коду:

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

Якщо другий зразок коду зберігається у рядку s, щось подібне до першого може бути створене виразом '#encoding=Latin\nexec"""%s"""'%__import__('zlib').compress(s). Може знадобитися виправити деякі символи, такі як нульові байти чи зворотні риски. Розпакований код становить майже 1000 800 байт, тож, можливо, він більше затуманений, ніж гольф ... але, принаймні, мені вдалося трохи розіграти кодування: від Latin1до Latin.

Пояснення

Програма працює з використанням індексів рядка як грубого способу відстеження станів розбору рядка. nФункція будує таблицю переходів. Спочатку він розбирає рядок і знаходить усі екземпляри двох видів переходів. По-перше, є переходи, які не передбачають додавання чергової літери до рядка. Наприклад, *перехід від a до початку кількісно вираженого виразу. По-друге, є переходи додавання символу, які просто рухаються вперед на один індекс. Тоді обчислюється транзитивне закриття переходів без символів, які замінюються пунктами призначення 1-символьних переходів. Таким чином, він повертає відображення індексу та символу до набору індексів.

Основна функція виконує BFS над можливими вхідними рядками. Він відстежує набір усіх можливих індексів для будь-якого фрагмента рядка, який він розглядає. Ми зацікавлені в пошуку - це держави, які приймаються або обома регулярними виразами, або одним, а не іншим. Щоб показати, що регулярний вираз прийнятий, необхідно лише знайти один можливий шлях переходів до кінця шаблону. Але щоб показати, що одного відкидають, необхідно проаналізувати всі можливі шляхи. Отже, щоб визначити, чи множини можливих станів для візерунка A і візерунка B вже охоплені тими, які були проаналізовані раніше, записуються пари одного стану в одному регулярному виразі, а множина всіх можливих станів в іншому. Якщо нових немає, тоді добре повернутися. Звичайно, також було б можливо, і менше символів,


Дуже хороша! Проходить усі тести в групах A і B (схоже, що немає класів символів). Не можу змушена версія роботи працювати, хоча або отримувати однаковий байт. У будь-якому випадку, ви можете вимагати x 0.92бонус, коли ви підраховуєте свій рахунок. І, звичайно, ласкаво просимо пояснення!
Елл

4

Хаскелл, 560 553 618

можуть отримати деякі бонуси в майбутньому.

це ще не повністю гольф.

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)

хвильове пояснення алгоритму:

reg!reg' повертає потрібний знак, один із "= <> x".

a#bє істинним, якщо не кожна відповідна рядок aтакож відповідає b.

c%reg- це список регулярних виразів, такий, що regвідповідає c:siff одному з регулярних виразів у збігу результатів s. я в основному частково відповідає регексу. за винятком, якщо cє '\0'. тоді він змушує regбільше не отримувати вклад, повертаючись, []якщо regпотрібно отримати більше вкладних даних, щоб відповідати [""]інакше.

#працює, генеруючи кінцевий список усіх можливих "станів регулярного виразів", два повторних виводу будуть знаходитися після довільної рядки. то для того, щоб перевірити, чи перевіряємо a<bми погоду, чи існує "регулярний стан", який aповністю відповідає, але bне повністю відповідає.


Класно! Ви, очевидно, на правильному шляху. Однак зараз тест не проходить B4.
Ell
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.