正規表現パターンの半順序付け


25

この課題のために、部分文字列だけでなく文字列全体がパターンと一致する場合、正規表現パターンは文字列と一致すると言います。

与えられた2つの正規表現のパターン  A  と  Bは、我々はと言う  Aが  され、より専門よりも  B   で一致したすべての文字列ならば  Aは  またで一致している  B   の周りに他の方法ではありません。私たちは、と言う  Aが  あると同等に  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は  比類のないです、我々はさらに、2つの場合を区別することができます:

  • A  と  Bが  ある互いに素AB文字列がそれらの両方にマッチしていないことを意味し、)。
  • A  及び  Bは  れる交差ABをいくつかの文字列の両方にマッチしていることを意味します)。

チャレンジ

正規表現パターンのペアを受け取り、上記の順序を使用してそれらを比較するプログラムまたは関数を作成します。二つのパターンである場合つまり、  A  と  B、プログラムが判断する必要があるかどうかを  A < B、  A > B
A = B  又は  AB

×92%ボーナス  パターンが比較できない場合に、プログラムがパターンが交差しているかどうかを判断すると、追加のボーナスが与えられます。

入出力

プログラムは、以下で定義するフレーバーの2つの正規表現パターンを文字列として受け入れる必要があります。関数の引数または同等のメソッドとして、コマンドラインであるSTDINから入力を読み取ることができます。パターンが有効であると仮定することができます。

プログラムは、比較の結果に応じて、正確に4つの異なる出力(または上記のボーナスを使用する場合は5つの異なる出力)の1つを生成する必要があります(正確な出力はユーザー次第です)。出力をSTDOUTに書き込むことができます。、関数の結果として返すか、同等のメソッドを使用します。

正規表現の味

任意の正規表現機能をサポートできます、次のものをサポートする必要があります。

  • 交替|
  • 定量化*
  • (およびでグループ化)ます。
  • 任意の文字(改行を除く可能性がある)とを一致させます.
  • (オプション:×80%ボーナス)  単純な文字クラスと否定された文字クラスをそれぞれ[…][^…]で照合します。定義済みの文字クラス(例:)をサポートする必要はありませんが、[:digit:]文字範囲をサポートする必要があります。
  • エスケープする文字\。少なくとも特殊文字(例:)|*().[^-]\と、できれば他のフレーバーの一般的な特殊文字(例:)をエスケープすることは可能ですが、非特殊文字を{}エスケープするときの動作は指定されていません。特に、\n改行などの特別なエスケープシーケンスをサポートする必要はありません。考えられる実装は、後に続く文字を\リテラルとして取得することです。

どのリテラルとも一致しない入力文字の存在を想定できます(つまり、.文字クラスとのみ一致し、否定されます)。

追加の規則

  • 正規表現ライブラリまたは組み込みの正規表現機能は、文字列の照合と置換の目的にのみ使用できます。
  • 照合規則などのロケール関連の問題は無視してもかまいません。
  • 明白なことを述べるには、プログラムを終了する必要があります。典型的なパターンが与えられた場合、妥当な時間内に実行する必要があります(明らかに1時間以内、できればずっと短い時間)。

得点

これはコードゴルフです。スコアは、コードサイズ(バイト単位)と任意のボーナスです。最低スコア勝ち。

テストケース

テストケースの形式は次のとおりです。

<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されます。2つの形式言語が同型かどうかを判断するのは十分に困難です。ある言語が別の言語のサブ言語であるかどうかを判断することは、完全な学部CSプロジェクトです。@ ___ @
COTO 14

無効な正規表現パターンに対して指定された動作はありますか?
ポールギュヨ14

@PaulGuyotいいえ。パターンが有効であると想定できます。
エルは

1
疑問に思う-あなたは自分で勝ったと書いたのですか(コードゴルフの質問に対してどれだけ実現可能かを見るため)、そうではなかったのですか?
誇りに思ってhaskeller 14

1
@proudhaskeller私がやった; テストスニペットを作成しました。これは難しい挑戦であり、ここにはワンライナーはありませんが、ゴルフはできます。
エル14

回答:


10

Python 2、522バイト* .92 = 480.24 537.28

編集2:-60バイト

編集:説明を追加しました。

定義された関数であり、f引数戻るように、2つのパターン文字列を取り'<''=''>''|'、または'X'。すべてのテストケースを処理するのに1分もかかりません。

コードは、以下からなるが、と\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

2番目のコードサンプルが文字列sに格納されている場合、最初のサンプルに似たものが式によって生成されます'#encoding=Latin\nexec"""%s"""'%__import__('zlib').compress(s)。nullバイトやバックスラッシュなどの一部の文字を修正する必要がある場合があります。解凍されたコードはほぼ1000 800バイトなので、おそらくゴルフよりも難読化されています...しかし、少なくともエンコードを少しはゴルフできました:from Latin1からLatin

説明

このプログラムは、文字列のインデックスを使用して、文字列の解析状態を追跡する粗雑な方法として機能します。このn関数は遷移表を作成します。まず、文字列を解析し、2種類の遷移のすべてのインスタンスを見つけます。最初に、文字列に別の文字を追加する必要のないトランジションがあります。たとえば、a *から数量化された式の先頭にジャンプします。第二に、キャラクターを追加するトランジションがあります。これは、インデックスを1つ進めるだけです。次に、非文字遷移の推移的閉包が計算され、1文字遷移の宛先に置き換えられます。したがって、インデックスと文字のインデックスのセットへのマッピングを返します。

メイン関数は、可能な入力文字列に対してBFSを実行します。考慮している文字列のフラグメントについて、可能なすべてのインデックスのセットを追跡します。私たちが見つけることに興味を持っているのは、両方の正規表現で受け入れられる状態、または一方ではなく他方で受け入れられる状態です。正規表現が受け入れられたことを示すために必要なのは、パターンの最後までの遷移の1つの可能なパスを見つけることだけです。ただし、拒否されたことを示すには、考えられるすべての経路を分析する必要があります。したがって、パターンAとパターンBの可能な状態のセットが以前に分析されたものですでにカバーされているかどうかを判断するために、1つの正規表現の単一の状態と他のすべての可能な状態のセットのペアが記録されます。新しいものがない場合は、戻っても構いません。もちろん、それも可能だろうし、キャラクターが少なくて、


非常に素晴らしい!グループ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一致するすべての文字列がによっても一致しない場合にのみtrue bです。

c%regは、出力の正規表現の1つがreg一致するc:s場合にのみ一致する正規表現のリストですs。私は基本的に正規表現に部分的に一致しています。場合を除いてcあります'\0'。それからそれreg以上入力を取得しないように強制し、一致するためにさらに入力を取得[]するreg必要があるかどうかを返し[""]ます。

#すべての可能な「正規表現状態」の有限リストを生成することにより機能し、2つの正規表現は任意の文字列の後になります。次に、a<b天気をチェックするかどうかを確認するために、a完全に一致しているが完全には一致していbない「正規表現状態」があります。


クール!あなたは明らかに正しい軌道に乗っています。ただし、現在はtestに失敗していますB4
エル14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.