正则表达式模式的部分排序


25

出于这个挑战的目的,我们说如果整个字符串(而不只是子字符串)都与该模式匹配,字符串匹配。

给定两个正则表达式模式   和  ,我们说  一个  是更专业比    ,如果每一个被匹配的字符串  一个  也是由匹配    左右,但没有别的办法。我们说,  一个  是等同于   如果两个模式匹配完全相同的一组字符串。如果两个模式都不比另一个更专业,或者它们都不等效,那么我们就说  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  无可比拟时,我们可以进一步区分两种情况:

  •  和   是不相交的),这意味着没有字符串由两者相匹配。
  •  和   的相交),这意味着有些字符串由两个相匹配。

挑战

编写一个使用一对正则表达式模式的程序函数,并按上述顺序对其进行比较。也就是说,如果两个图案是   和  ,程序应该确定是否  < ,  >
=  或 

×92%奖金  如果图案无法比拟时,程序确定它们是相交还是相交,则将提供额外的奖金。

输入输出

该程序应接受以下两种定义的形式的两个正则表达式模式作为字符串。你可以读取通过输入STDIN,所述命令行,作为函数参数等效的方法。您可以假设这些模式是有效的。

程序应根据比较结果(确切的输出由您决定)产生正好四个不同的输出之一(或者,如果您要获得上述奖金,则可以产生五个不同的输出)之一。您可以将输出写入STDOUT,将其作为函数的结果返回或使用等效的方法

正则香料

您可以支持所需的任何正则表达式功能,但必须支持以下功能:

  • 交替|
  • 定量*
  • (分组)
  • 匹配任何字符(可能不包括换行符).
  • (可选:×80%奖金)分别使用和 匹配简单字符类和否定字符类。您不必支持任何预定义的字符类(例如),但应支持字符范围。[…][^…][:digit:]
  • 字符转义\。至少应该保留特殊字符(即|*().[^-]\),最好也保留其他风味的常见特殊字符(例如{}),但是转义非特殊字符时的行为是不确定的。特别是,您不必支持特殊的转义序列,例如\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>代表空模式。

A.基本模式

B.复杂模式

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

无效的正则表达式模式是否有任何指定的行为?
Paul Guyot 2014年

@PaulGuyot否。您可以假设这些模式有效。
2014年

1
我想知道-您写自己赢了(看一个代码高尔夫问题有多可行),不是吗?
骄傲的haskeller 2014年

1
@proudhaskeller我做到了;我写了测试代码段。这是一个艰巨的挑战,这里不会有任何衬板,但可以打高尔夫球。
2014年

Answers:


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字节,因此它可能比打高尔夫球更难懂了……但是至少我设法打了一下编码:从Latin1Latin

说明

该程序通过使用字符串的索引作为粗略的方法来工作,以跟踪解析字符串的状态。该n函数生成过渡表。首先,它解析字符串并找到两种过渡的所有实例。首先,有些转换不涉及在字符串中添加另一个字母。例如,从a跳到*量化表达式的开头。其次,存在添加字符的过渡,这些过渡只是向前移动一个索引。然后,计算无字符转换的传递闭包,并用这些闭合替换1字符转换的目的地。因此,它返回索引和字符到一组索引的映射。

main函数对可能的输入字符串执行BFS。它跟踪正在考虑的字符串的任何片段的所有可能索引的集合。我们感兴趣的是找到被两个正则表达式接受的状态,或者被一个正则表达式接受的状态。为了表明正则表达式被接受,只需要找到一条过渡到模式结尾的可能路径即可。但是,为了表明一个被拒绝,有必要分析所有可能的路径。因此,为了确定模式A和模式B的可能状态集是否已经被之前已经分析过的状态所覆盖,记录一个正则表达式中的单个状态对和另一个中的所有可能状态的对。如果没有新的,则可以返回。当然,也有可能,而且字符更少,


非常好!通过了A组和B组中的所有测试(似乎没有字符类。)但是,我无法获得压缩版本,也无法获得相同的字节数。无论哪种方式,您都可以x 0.92在计算分数时申请奖金。并且,当然,欢迎您提供解释!
2014年

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匹配的字符串也都由匹配,则为true b

c%reg是一个正则表达式列表,可以reg匹配c:s输出中的其中一个正则表达式匹配s。我基本上是部分匹配正则表达式。除非c'\0'。则它强制reg不获得更多输入,[]如果reg必须获得更多输入以匹配[""]则返回。

#通过生成两个正则表达式将位于任意字符串之后的所有可能“正则表达式状态”的有限列表来工作。然后检查a<b我们是否检查天气,有一个“正则表达式状态”,其中a完全匹配,但b不完全匹配。


凉!您显然在正确的轨道上。但是,现在它无法测试B4
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.