このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

コンウェイの和スター公式とデリミータで区切ったデータ

正規表現パターンに対して次が成立します。

  • (A | B)* = (A*B)* (A*)

AとBは任意のパターン、イコールは「左辺にマッチする文字列は右辺にもマッチし、右辺にマッチする文字列は左辺にもマッチする」という意味。

もう少し一般的な文脈では、これをコンウェイ(Conway)の和スター公式(sum-star equation)と呼びます。右辺の「|」を「+」に書き換えると、(A + B)* なので、和とスターの組み合わせから和を除く手順だと思ってもいいでしょう。

この和スター公式は憶えにくいし、憶えても忘れる。そこで、具体的で馴染みのある例を考えました。


DataChar ::= [-_a-zA-Z0-9]
DelmChar ::= [, ]
EndOfData ::= [.\n] /* 注:[]のなかでは\.としなくてもよい。 */

区切り文字を「コンマまたは空白」として、データ項目(フィールド)を並べたデータ表現(レコード)を考えます。レコードの終わりはピリオドか改行です。


Item ::= DataChar* DelmChar
Record ::= Item* EndOfData

別に不思議はないですよね。次はレコードの例です。


hello_world,123 456, Apple,.

項目に区切ると、"hello_world", "123", "456", "", "Apple"の5項目です。Appleの直前の空白により空な項目が1つ生まれてしまったのです。全部コンマで書けば事情がわかりやすいでしょう。


hello_world,123,456,,Apple,.

Appleの直後のコンマも気になりますが、このコンマも含めて項目なので省略できません。最後の区切り文字は省略可能だとすると次の定義となります。


Record ::= (DataChar* DelmChar)* DataChar*

(DataChar* DelmChar) が最後に区切り文字を持つ項目、DataChar* が区切り文字を持たない最後の項目です(これはなくてもよい)。今定義したRecordの構文をもとに、構文チェッカーを作りましょう。先頭からピリオドまたは改行までの文字列が“構文的に正しいレコード”かどうかを判定するプログラムです。

んん、 待てよ。

“構文的に正しいレコード”って、実は何だって正しくなってしまうのでは?

データ文字でも区切り文字でもない変な文字が出てこない限りは、どんな文字列でも“構文的に正しいレコード”となります。ちょっと考えればわかりますよね。つまり、実は次の定義でも同じこと。


Record ::= (DataChar | DelmChar)*

正規表現パターンとしての効果が同じことをイコールで示すなら:


(DataChar | DelmChar)* = (DataChar* DelmChar)* DataChar*

はい、これが冒頭で示した和スター公式の例です。

[追記]

正規表現を普通っぽく /(a|b)*/ のように書くとして、/(a|b)*/ == /(a*b)*a*/ のような等式を使えるプログラミング言語はないと思いますよ。等式の判定ができないわけじゃないけど、やるとするなら次のどちらかでしょう。

  • 正規表現から標準化(canonicalize)されたオートマトンを作って、オートマトン構造(辺ラベル付き有限グラフ)が同等(isomorphic)であるかどうか判定する。
  • 正規表現の代数(クリーネ代数)は、ホーン等式の公理系を持つので、ホーン等式的推論系を作り、与えられた等式をこの推論系に証明させる。

2番目のほうは、現実的に可能かどうかは知りません。いずれにしても、/(a|b)*/ == /(a*b)*a*/ を使う場面や需要がないでしょう。

[/追記]