Частичное упорядочение шаблонов регулярных выражений


25

Для этой задачи мы говорим, что шаблон регулярного выражения соответствует строке, если вся строка соответствует шаблону, а не только подстроке.

Даны два регулярных выражений модели   и  B , мы говорим , что   является более специализированы , чем  B ,   если каждая строка, подобран  А  также соответствует по  B ,   но не наоборот. Мы говорим , что   является эквивалентом для  B ,  если обе модели соответствуют точно такой же набор строк. Если ни одна картина более специализирована , чем другие и они не эквивалентны, мы говорим , что   и  B  является несравнимы .

Например, шаблон Hello, .*!более специализирован, чем .*, .*!; шаблоны (Hello|Goodbye), World!и Hello, World!|Goodbye, World!эквивалентны; и шаблоны Hello, .*!и .*, World!несопоставимы.

Отношение «более специализированный, чем» определяет строгий частичный порядок на множестве шаблонов регулярных выражений. В частности, для всех шаблонов  A  и  B верно одно из следующего:

  • A  более специализирован, чем  B  ( A < B ).
  • B  более специализирован, чем  A  ( A > B ).
  • А  и  В  эквивалентны ( А = В ).
  • A  и  B  несравнимы ( AB ).

Когда  A  и  B  несопоставимы, мы можем далее различать два случая:

  • A  и  B  не пересекаются ( AB ), что означает, что ни одна из них не соответствует ни одной строке.
  •  И  B  являются пересекающиеся ( AB ), что означает , что некоторые строки совпадают с обоими.

Вызов

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

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

Вход и выход

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

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

Regex Flavor

Вы можете поддерживать любые функции регулярных выражений, которые вам нравятся, но вы должны поддерживать следующие:

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

Вы можете предположить существование входного символа, который не может быть сопоставлен никаким литералом (т. Е. Ему могут соответствовать только .классы символов с отрицанием.)

Дополнительные правила

  • Вы можете использовать библиотеки регулярных выражений или встроенные функции регулярных выражений только с целью сопоставления и замены строк.
  • Вы можете игнорировать любые проблемы, связанные с локалью, такие как правила сортировки.
  • Чтобы заявить очевидное: ваша программа должна завершиться. Он должен выполняться за разумное количество времени с учетом типовых паттернов (определенно, не более часа, предпочтительно намного меньше).

счет

Это код-гольф. Ваша оценка является продуктом из размера кода , в байтах, и любой из бонусов . Самый низкий балл побеждает.

Тестовые случаи

Формат контрольных примеров следующий:

<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 *к началу количественного выражения. Во-вторых, есть переходы добавления символа, которые просто продвигаются на один индекс вперед. Затем вычисляется транзитивное замыкание безсимвольных переходов, которые заменяют места назначения односимвольных переходов. Таким образом, он возвращает отображение индекса и символа на набор индексов.

Основная функция выполняет BFS над возможными входными строками. Он отслеживает набор всех возможных индексов для любого фрагмента строки, который он рассматривает. Мы заинтересованы в том, чтобы находить состояния, которые либо принимаются обоими регулярными выражениями, либо одним, а не другим. Чтобы показать, что регулярное выражение принято, необходимо найти только один возможный путь переходов в конец шаблона. Но чтобы показать, что кто-то отвергнут, необходимо проанализировать все возможные пути. Поэтому, чтобы определить, покрыты ли уже наборы возможных состояний для шаблона A и шаблона B теми, которые были проанализированы ранее, записываются пары одного состояния в одном регулярном выражении и набор всех возможных состояний в другом. Если новых нет, то можно вернуться. Конечно, это также было бы возможно, и меньше символов,


Очень хорошо! Проходит все тесты в группах A и B (кажется, нет классов символов). Однако я не могу заставить работать сжатую версию или получить тот же счетчик байтов. В любом случае, вы можете претендовать на x 0.92бонус, когда вы рассчитываете свой счет. И, конечно, объяснение приветствуется!
Ell

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:sтогда, когда одно из регулярных выражений в выходных совпадениях s. я в основном частично соответствует регулярному выражению. кроме если cесть '\0'. тогда он вынуждает regне получать больше ввода, возвращая []if , если regнужно получить больше ввода для соответствия и в [""]противном случае.

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


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