2012-07-26 42 views
5

この正規表現は回文に一致します。 ^((.)(?1)\2|.?)$正規表現エンジンは再帰的サブパターンで正規表現をどのように解析しますか?

は、それがどのように動作するかのまわりで私の頭をラップすることはできません。 いつ再帰が終了し、正規表現が再帰的サブパターンから壊れて"|.?"部分に行くか?

ありがとうございました。

編集:私は\2(?1)

(?1)を説明しなかった申し訳ありません - (自身に)最初のサブパターンを指す

\2から(.)

第二サブパターンの一致への後方参照

上記の例はPHPで書かれています。マッチの両方の「アバ」(無ミッド回文文字)と「ABCBAは」 - ミドル、非反映文字

+0

これは実際には "実用的な"問題ではないので、これはおそらくプログラマの方が良いでしょう。 – Jeff

+0

'。?'はたぶん奇数長の文字列です。 – nhahtdh

+0

申し訳ありませんが、 '(?1)'と '\ 2'は何ですか? –

答えて

3

^((.)(?1)\2|.?)$

^$先頭とそれぞれ文字列の最後をアサートしています。私たちはもっと面白いある、間でコンテンツを見てみましょう:最初の部分(.)(?1)\2

((.)(?1)\2|.?) 
1------------1 // Capturing group 1 
2-2   // Capturing group 2 

見て、我々はそれが最後に任意の文字、そしてその同じ文字にマッチしようとしていることがわかります(バックリファレンス\2(.)と一致する文字を参照)。真ん中では、キャプチャグループ1全体に対して再帰的に一致します(最初に1文字に一致する(.)と最後に同じ文字に一致する\2によって引き起こされる暗黙のアサーションがあります)。 2文字。最初の部分の目的は、文字列の同じ端を再帰的にチョッピングすることです。

.?の2番目の部分を見ると、1文字または0文字に一致することがわかります。これは、最初に文字列の長さが0または1の場合、または再帰的一致からの残存が0または1文字の場合にのみ一致します。 2番目の部分の目的は、文字列が両端から切り取られた後、空の文字列または単一の孤独な文字に一致させることです。

再帰的なマッチングは動作します:

  • 文字列全体は回文が^$によってアサート、渡すことでなければなりません。ランダムな位置からマッチングを開始することはできません。
  • 文字列が< = 1文字の場合、それは合格です。
  • 文字列が> 2文字である場合、それが受け入れられるかどうかは、最初の部分のみによって決定されます。一致すれば2つの端で切り刻まれます。
  • 一致する場合は残りの部分が2つの端でのみ切断され、長さが< = 1の場合は通過できます。
+0

返信いただきありがとうございます。私は、より多くの説明で元の投稿を編集しました。 '\ 2'は長さではありません。 – alexy2k

+1

'\ 2'は長さではありませんが、その文字を少なくとも2文字以上にする必要があります。なぜなら、1文字は'(。) 'と後方参照' \ 2'の両方に一致しないからです。 –

+2

最初の部分のために再帰するたびに、2つの終了文字が削除された文字列が処理されます。最終的に0または1文字に縮小され、2番目の部分が一致して再帰が停止します。 – Barmar

3

正規表現は、次の擬似コードと本質的に同等である:

palin(str) { 
    if (length(str) >= 2) { 
     first = str[0]; 
     last = str[length(str)-1]; 
     return first == last && palin(substr(str, 1, length(str)-2)); 
    } else 
     // empty and single-char trivially palindromes 
     return true; 
} 
1

私はPCREの正規表現のための任意の素敵なデバッグユーティリティを発見していません。より多くの私は見つけることができるバイトコードをダンプする方法でした:PerlはPCREよりも優れたデバッグツールを備えてい

$ pcretest -b 
PCRE version 7.6 2008-01-28 

    re> /^((.)(?1)\2|.?)$/x 
------------------------------------------------------------------ 
    0 39 Bra 
    3 ^
    4 26 CBra 1 
    9 6 CBra 2 
14  Any 
15 6 Ket 
18 6 Once 
21 4 Recurse 
24 6 Ket 
27  \2 
30 5 Alt 
33  Any? 
35 31 Ket 
38  $ 
39 39 Ket 
42  End 
------------------------------------------------------------------ 

echo 123454321 | perl -Mre=debug -ne '/^((.)(?1)\2|.?)$/x'を試してみてください。これは、各ステップでPCREのものと類似しているが、それはまた、各ステップを示し、いくつかのバイトコード、および入力の消費と残りの部分だけでなく、を与える:

Compiling REx "^((.)(?1)\2|.?)$" 
Final program: 
    1: BOL (2) 
    2: OPEN1 (4) 
    4: BRANCH (15) 
    5:  OPEN2 (7) 
    7:  REG_ANY (8) 
    8:  CLOSE2 (10) 
    10:  GOSUB1[-8] (13) 
    13:  REF2 (19) 
    15: BRANCH (FAIL) 
    16:  CURLY {0,1} (19) 
    18:  REG_ANY (0) 
    19: CLOSE1 (21) 
    21: EOL (22) 
    22: END (0) 
floating ""$ at 0..2147483647 (checking floating) anchored(BOL) minlen 0 
Guessing start of match in sv for REx "^((.)(?1)\2|.?)$" against "12321" 
Found floating substr ""$ at offset 5... 
Guessed: match at offset 0 
Matching REx "^((.)(?1)\2|.?)$" against "12321" 
    0 <> <12321>    | 1:BOL(2) 
    0 <> <12321>    | 2:OPEN1(4) 
    0 <> <12321>    | 4:BRANCH(15) 
    0 <> <12321>    | 5: OPEN2(7) 
    0 <> <12321>    | 7: REG_ANY(8) 
    1 <1> <2321>    | 8: CLOSE2(10) 
    1 <1> <2321>    | 10: GOSUB1[-8](13) 
    1 <1> <2321>    | 2: OPEN1(4) 
    1 <1> <2321>    | 4: BRANCH(15) 
    1 <1> <2321>    | 5:  OPEN2(7) 
    1 <1> <2321>    | 7:  REG_ANY(8) 
    2 <12> <321>    | 8:  CLOSE2(10) 
    2 <12> <321>    | 10:  GOSUB1[-8](13) 
    2 <12> <321>    | 2:  OPEN1(4) 
    2 <12> <321>    | 4:  BRANCH(15) 
    2 <12> <321>    | 5:   OPEN2(7) 
    2 <12> <321>    | 7:   REG_ANY(8) 
    3 <123> <21>    | 8:   CLOSE2(10) 
    3 <123> <21>    | 10:   GOSUB1[-8](13) 
    3 <123> <21>    | 2:   OPEN1(4) 
    3 <123> <21>    | 4:   BRANCH(15) 
    3 <123> <21>    | 5:    OPEN2(7) 
    3 <123> <21>    | 7:    REG_ANY(8) 
    4 <1232> <1>    | 8:    CLOSE2(10) 
    4 <1232> <1>    | 10:    GOSUB1[-8](13) 
    4 <1232> <1>    | 2:    OPEN1(4) 
    4 <1232> <1>    | 4:    BRANCH(15) 
    4 <1232> <1>    | 5:     OPEN2(7) 
    4 <1232> <1>    | 7:     REG_ANY(8) 
    5 <12321> <>    | 8:     CLOSE2(10) 
    5 <12321> <>    | 10:     GOSUB1[-8](13) 
    5 <12321> <>    | 2:     OPEN1(4) 
    5 <12321> <>    | 4:     BRANCH(15) 
    5 <12321> <>    | 5:      OPEN2(7) 
    5 <12321> <>    | 7:      REG_ANY(8) 
                 failed... 
    5 <12321> <>    | 15:     BRANCH(19) 
    5 <12321> <>    | 16:      CURLY {0,1}(19) 
                 REG_ANY can match 0 times out of 1... 
    5 <12321> <>    | 19:      CLOSE1(21) 
                  EVAL trying tail ... 9d86dd8 
    5 <12321> <>    | 13:       REF2(19) 
                  failed... 
                 failed... 
                 BRANCH failed... 
    4 <1232> <1>    | 15:    BRANCH(19) 
    4 <1232> <1>    | 16:     CURLY {0,1}(19) 
                REG_ANY can match 1 times out of 1... 
    5 <12321> <>    | 19:     CLOSE1(21) 
                 EVAL trying tail ... 9d86d70 
    5 <12321> <>    | 13:      REF2(19) 
                 failed... 
    4 <1232> <1>    | 19:     CLOSE1(21) 
                 EVAL trying tail ... 9d86d70 
    4 <1232> <1>    | 13:      REF2(19) 
                 failed... 
                failed... 
                BRANCH failed... 
    3 <123> <21>    | 15:   BRANCH(19) 
    3 <123> <21>    | 16:    CURLY {0,1}(19) 
               REG_ANY can match 1 times out of 1... 
    4 <1232> <1>    | 19:    CLOSE1(21) 
                EVAL trying tail ... 9d86d08 
    4 <1232> <1>    | 13:     REF2(19) 
                failed... 
    3 <123> <21>    | 19:    CLOSE1(21) 
                EVAL trying tail ... 9d86d08 
    3 <123> <21>    | 13:     REF2(19) 
                failed... 
               failed... 
               BRANCH failed... 
    2 <12> <321>    | 15:  BRANCH(19) 
    2 <12> <321>    | 16:   CURLY {0,1}(19) 
              REG_ANY can match 1 times out of 1... 
    3 <123> <21>    | 19:   CLOSE1(21) 
               EVAL trying tail ... 9d86ca0 
    3 <123> <21>    | 13:    REF2(19) 
    4 <1232> <1>    | 19:    CLOSE1(21) 
               EVAL trying tail ... 0 
    4 <1232> <1>    | 13:    REF2(19) 
    5 <12321> <>    | 19:    CLOSE1(21) 
    5 <12321> <>    | 21:    EOL(22) 
    5 <12321> <>    | 22:    END(0) 
Match successful! 
Freeing REx: "^((.)(?1)\2|.?)$" 

あなたが見ることができるように、Perlは最初にすべての入力を消費します(.)まで再帰が失敗した後、バックトラッキングを開始して、代替番号.?から第2ブランチを試し、最初の部分の残りの部分は\2です。

+0

なぜこのデバッグでは、6つの 'OPEN1'がありますが、対応して8つの' CLOSE1'がありますか? 「CLOSE1」も6ではないでしょうか? – revo

関連する問題