有限状態オートマトンで後方参照、先読み、および後読みをシミュレートする方法は?


26

単純な正規表現レクサーとパーサーを作成して、正規表現を取得し、その解析ツリーを生成しました。この解析ツリーから非決定性の有限状態オートマトンを作成することは、基本的な正規表現では比較的簡単です。ただし、後方参照、先読み、および後読みをシミュレートする方法について頭を悩ますことはできません。

私がシミュレートA先読みすることを私は理解紫龍の本で読んだことから、正規表現のどこ一致するものが正規表現の一致が続いている場合に限り、一致している、あなたは非決定性有限を作成/εに置き換えられる状態オートマトン。同じことをする決定性有限状態オートマトンを作成することは可能ですか?r/srs/ε

ネガティブな先読みと後読みのシミュレーションはどうですか?これを行う方法を詳細に説明しているリソースにリンクしていただければ幸いです。



回答:


21

まず第一に、後方参照は非正規言語を記述することができるため、有限オートマトンではシミュレートできません。例えば、([ab]^*)\1マッチさえ文脈自由ではありません。{www{ab}}

先読みと後読みは、ここでは入力全体のみを照合するため、有限オートマトンの世界では特別なものではありません。したがって、「チェックするだけで消費しない」という特別なセマンティックは無意味です。単にチェックおよび消費式を連結および/または交差させ、結果のオートマトンを使用します。アイデアは、入力を「消費」して結果を状態に保存しながら、先読み式または後読み式をチェックすることです。

正規表現を実装する場合、オートマトンを介して入力を実行し、一致の開始インデックスと終了インデックスを取得します。これは非常に異なるタスクなので、有限オートマトンの構築は実際にはありません。先読み式または後読み式が消費されているかのようにオートマトンを構築し、インデックスを保存する各リソースを変更します。それに応じて報告する。

たとえば、後読みを考えてみましょう。暗黙的に消費する「全一致」正規表現に対して正規表現のチェックを同時に実行することにより、正規表現のセマンティクスを模倣できます。後読み式のオートマトンが最終状態にある状態からのみ、保護された式のオートマトンを入力できます。たとえば、正規表現/(?=c)[ab]+/が完全なアルファベットである仮定)-正規表現{ a b c } c { a b } + { a b c }{abc} -と一致する可能性がある{abc}c{ab}+{abc}

ここに画像の説明を入力してください
[ ソース ]

そして、あなたはする必要があります

  • q 2(最初またはq 2から)を入力するたびに、現在のインデックスをとして保存します。q2q2
  • q 2を押す(離れる)たびに、から現在のインデックス(1)への(最大)一致を報告します。1q2

オートマトンの左側の部分が、[abc]*およびc(反復)のそれぞれの標準オートマトンの並列オートマトンであることに注意してください。

先読みも同様に処理できます。「メイン」オートマトンに入るときはインデックスを覚えておく必要があります。メインオートマトンを離れて先読みオートマトンに入るときはインデックスjを覚えて、先読みオートマトンのファイナルをヒットしたときにのみiからjへの一致を報告する必要があります状態。jij

非決定性はこれに固有のものであることに注意してください。メインオートマトンとルックアヘッド/ビハインドオートマトンは重複する可能性があるため、一致するものを後で報告するため、またはバックトラックするためにそれらの間のすべての遷移を保存する必要があります。


11

正規表現エンジンの実装の背後にある実用的な問題に関する信頼できる参照は、Russ Coxによる一連の3つのブログ投稿です。後方参照は、あなたの言語が非正規にするために、そこに記載されているように、彼らが使用して実装されているバックトラックを

先読みと後読みは、正規表現パターンマッチングエンジンの多くの機能のように、文字列が言語のメンバーであるかどうかを決定するというパラダイムにはまったく適合しません。正規表現ではなく、通常、より大きな文字列内の部分文字列を検索しています。「一致」は言語のメンバーである部分文字列であり、戻り値は大きい文字列内の部分文字列の開始点と終了点です。

先読みと後読みのポイントは、非正規言語に一致する機能を導入することではなく、一致したサブストリングの開始点と終了点をエンジンが報告する場所を調整することです。

http://www.regular-expressions.info/lookaround.htmlの説明に依存しています。この機能をサポートする正規表現エンジン(Perl、TCL、Python、Rubyなど)はすべて、バックトラッキングに基づいているようです(つまり、通常の言語よりもはるかに大きな言語セットをサポートしています)。彼らは、タスクを実行するために実際の有限オートマトンを構築しようとするのではなく、バックトラッキングの比較的「単純な」拡張としてこの機能を実装しているようです。

ポジティブ・ルックアヘッド

正の先読みの構文は(?=regex)です。だから、例えばq(?=u)一致しq、それが続いている場合にのみu、とは一致しませんu。彼らはこれをバックトラックのバリエーションで実装していると思います。正の先読みの前に式のFSMを作成します。それが一致したら、終了位置を覚えて、ポジティブルックアヘッド内の表現を表す新しいFSMを開始します。それが一致する場合、「一致」がありますが、肯定的な先読み一致が開始された位置の直前で一致が「終了」します。

バックトラックなしでは難しいのは、入力の先読みを開始するポイントを覚えて、一致が完了した後に入力テープをこの位置に戻す必要があるということだけです。

負の先読み

負の先読みの構文は(?!regex)です。したがって、たとえば、後に続かない場合にのみq(?!u)一致quます。これは、q後に他の文字が続くかq、文字列の最後にある可能性があります。先読み式のNFAを作成し、NFAが後続の文字列と一致しない場合にのみ成功することにより、これが実装されると思います。

バックトラックに頼らずにそれを行いたい場合は、先読み表現のNFAを無効にし、肯定的な先読みを処理するのと同じ方法で処理します。

ポジティブルックビハインド

(?<=)(?=q)uuqqnnn

正規表現で終わる文字列」と、後読み演算子の前にある正規表現の任意の部分との交差を取ることにより、バックトラッキングなしでこれを実装できる場合があります。ただし、後読み正規表現は、入力の現在の始まりよりもさらに後ろを見る必要があるため、これは注意が必要です。

負の後読み

負の後読みの構文は(?<!regex)です。したがって、たとえば、に(?<!q)u一致しますがu、先頭にが付いていない場合のみですq。合っているでしょうそれはそうuではumbrellaudoubtはなく、u中にquick。繰り返しますが、これは長さ計算によって行われるように見える正規表現を、との一致を調べる、その多くの文字をバックアップする正規表現が、今後読みが一致する場合にはマッチ全体を失敗します。

正規表現の否定を取得し、後読みのために行うのと同じことを行うことで、バックトラッキングなしでこれを実装できる場合があります。


5

少なくとも後方参照の場合、これは不可能です。たとえば、正規表現は正規(.*)\1ではない言語を表します。つまり、この言語を認識する有限オートマトン(決定的または非決定的)を作成することは不可能です。これを正式に証明したい場合は、ポンピング補題を使用できます。


4

私はこれを自分で調べてきましたが、Alternating Finite Automatonを使用して先読みを実装できるはずです。先読みに遭遇すると、先読みと式の残りの両方を非決定的に実行し、両方のパスが受け入れられる場合にのみ受け入れます。AFAを適切なブローアップで(したがってDFAに)NFAに変換できますが、キャプチャグループで明白な構造がうまく機能することを確認していません。

固定幅の後読みは、バックトラッキングなしで完全に可能になるはずです。してみましょうnは幅があること。後読みが始まったあなたのNFAのポイントから始めて、あなたが後方にそう見ている状態を分割したいことで終わった後読みにすべてのパスn個の状態の文字分だけ後読みに行ってきました。次に、それらの状態の先頭に先読みを追加します(必要に応じてAFAからNFAにサブグラフをすぐにコンパイルします)。

他の人が言及したように、後方参照は規則的ではないため、有限オートマトンによって実装することはできません。実際、それらはNP完全です。私が取り組んでいる実装では、迅速なyes / noマッチングが最重要であるため、後方参照をまったく実装しないことにしました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.