読者です 読者をやめる 読者になる 読者になる

ネストする正規表現

『メールアドレスはネストしたコメントを持つことができるので正規表現で表わすのは不可能である』
というのは本当だろうか。


正規表現のネストが可能かというのは、実際のところは正規表現の定義と実装による。
PerlRubyだと、これは可能だ。


試しに『f(g(1))』のようなC言語タイプの簡易的な関数呼び出しにマッチする正規表現を書いてみよう。

Perl

(??{$code})という形で正規表現を再起的に埋め込むことができる。

#!/usr/bin/perl
use strict;
use warnings;

my @samples = (
  "f()",
  "f(g(a))",
  "f(g(h(123)))",
  "f(())",
  "f(g()",
  "f(g()))",
);

my $re;
$re = qr/
  [a-z]+
  \(
    ( (??{$re}) | [a-z]+ | [0-9]+| )
  \)
/x;

for my $str (@samples){
  my $result = $str =~ /^$re$/g ? "Match" : "Not Match";
  print "$str : $result\n";
}

実行結果

[mikeda@test01 work]$ perl func_match.pl
f() : Match
f(g(a)) : Match
f(g(h(123))) : Match
f(()) : Not Match
f(g() : Not Match

Perl5.10からは別の書き方もできるというウワサも。また調査してみる。

Ruby

Rubyでも同じ書き方ができる(はず。未確認)が今回は1.9から採用された正規表現ライブラリ、鬼車の機能を使ってみよう。
鬼車では(?.....)でくくった部分的な正規表現を、\gという書式で呼び出すことができる。ネストも可能だ。

#!/usr/local/bin/ruby

samples = [
  "f()",
  "f(g(a))",
  "f(g(h(123)))",
  "f(())",
  "f(g()",
]

re = /\A
  (?<func>
      [a-z]+
      \(
        (\g<func> | [a-z]+ | [0-9]+ |)
      \)
  )
\z/x

samples.each do |str|
  result = re.match(str) ? "Match" : "Not Match"
  puts "#{str} : #{result}"
end

この機能は1.9以降でなければ使えない。

[mikeda@test01 ruby]$ ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-linux]

実行結果

[mikeda@test01 work]$ ruby func_match.rb
f() : Match
f(g(a)) : Match
f(g(h(123))) : Match
f(()) : Not Match
f(g() : Not Match

これらがわかりやすいかどうかは別として、枯れていると思われがちな正規表現もまだまだ進化を繰り返しています。
正規表現、奥深ス