Perl Hackers Hub

第46回 Perl 5.26で変わること(3)

この記事を読むのに必要な時間:およそ 5 分

(1)こちら⁠2)こちらから。

Perl 5.26で新たに追加されたもの

このように非互換性の話ばかりしているとPerl 5.26へのアップグレードをためらう声も出てきそうですが,Perl 5.26にはセキュリティ上のメリットだけでなく,性能的なメリットや便利な新機能などもあります。

インデント付きヒアドキュメント

ある程度まとまった分量の,特に複数行にわたる文字列を扱いたい場合は,ヒアドキュメントを利用するのが定番です。

print <<"END";
$name さん,こんにちは。
...
END

ただ,従来のヒアドキュメントはインデントの概念が存在していなかったため,メソッドや条件文に囲まれていると見栄えが悪くなる欠点がありました。

sub print_hello {
    my $name = shift;
    if ($name) {
        print <<"END";
$name さん,こんにちは。
...
END
    }
}

Perl 5.26では新たにインデント付きのヒアドキュメントが導入されました。これを使うと,範囲内のすべての行が終端文字列のインデントに合わせてインデントされているとみなされます。

sub print_hello {
    my $name = shift;
    if ($name) {
        # 出力される内容は前と同じ
        print <<~"END";
            $name さん,こんにちは。
            ...
            END
    }
}

コンフリクトを起こしているソースコードの検出

Gitなどのバージョン管理システムを利用して開発を進めていると,ときにはコンフリクトを起こしてしまうことがあります。もちろんそのようなコンフリクトはすぐに解消すべきものですが,うっかりしているとコンフリクトが残ったままテストを走らせてしまうことがあるものです。

<<<<<<< HEAD
print "Hello, World!";
=======
print "Goodbye, World!";
>>>>>>> test

従来のPerlは,このようなソースコードを読み込むと暗黙のうちに空の終端文字列を持つヒアドキュメントとみなして解釈していたのですが,Perl 5.26では正しくバージョン管理システムのマークを読み取って,次のようなエラーメッセージを出すようになりました。

Version control conflict marker at test.pl line 1, near
"<<<<<<<"
Version control conflict marker at test.pl line 3, near
"======="
Version control conflict marker at test.pl line 5, near
">>>>>>>"

Unicode 9.0サポート

Perl 5.26ではUnicode 9.0がサポートされています。Unicode 9.0にはオリンピックで活躍するであろう金銀銅メダルの絵文字などが追加されました。

/xx正規表現修飾子

従来からある/x正規表現修飾子を使うと,空白文字の扱いが変わることを代償に,正規表現中に改行やコメントを追加して読みやすくできます。ただし,/x修飾子は文字クラスの中身までは対象にしません。そのため,次のようなコードは,与えられた文字列によっては一見うまく動いているように見えますが,対象となる文字列に空白が含まれている場合,その空白も一緒に抜き出してしまいます。

my $string = "Hello, World!";
my @words = $string =~ / ([ a-z A-Z ]+) /gx;
say join ";", @words; # Hello; World

Perl 5.26で導入された/xx修飾子を使うと,このような文字クラス内の空白とタブも(バックスラッシュでエスケープされていない限り)無視できます。

my $string = "Hello, World!";
my @words = $string =~ / ([ a-z A-Z ]+) /gxx;
say join ";", @words; # Hello;World

特殊変数@{^CAPTURE}など

マッチしても,しなくてもよいという正規表現で,実際に何がマッチしたかを調べるのは意外と面倒なものです。

たとえば,⁠a: b c」というコロンと空白で3つの部分に区切られた文字列からデータを取り出すことを考えてみます。あまり細かいことを考えずに書けば,次のようなコードになるでしょうか。

use 5.010;
my $string = "a: b c";
$string =~ /\A(.+?): (.+?) (.+)\z/;
say "$1, $2, $3"; # a, b, c

今,仕様が変わって3つ目の部分には値が入らない場合があることがわかりました。正規表現の最後を調整しましょう。

my $string = "a: b";
$string =~ /\A(.+?): (.+?)(?: (.+))?\z/;
say "$1, $2, $3"; # a, b,

出力結果の最後に余計な,が付いているのは見栄えが良くないですし,warningsプラグマを有効にしていると警告が出るのもいただけません。

このような場合,$3の値が定義されているかどうかで挙動を変えてもよいのですが,$0から始まる特殊変数が何文字目から始まり何文字目で終わるという情報を格納している特殊変数@-@+を見ると,いくつマッチしたかを調べられます注8)⁠また,マッチした値はsubstr関数を使って導き出せます。

my $string = "a: b";
$string =~ /\A(.+?): (.+?)(?: (.+))?\z/;
say $#-; # 2
my $first = substr($string, $-[1], $+[1] - $-[1]); # $1

検索の場合はマッチした値を直接配列で受け取ることもできますが,この受け取り方は必ずしも期待どおりの値を受け取れるとは限りません。

my $string = "a: b";
my @capture = $string =~ /\A(.+?): (.+?)(?: (.+))?\z/;
say join ", ", @capture; # a, b,

Perl 5.26で新設された@{^CAPTURE}特殊変数を使うと,この処理をもっと直感的に書けます。

use 5.026;
my $string = "a: b";
$string =~ /\A(.+?): (.+?)(?: (.+))?\z/;
say scalar @{^CAPTURE}; # 2
say join ", ", @{^CAPTURE}; # a, b
say ${^CAPTURE}[0]; # a
# この特殊変数には1つバグがあるが
# 次のバージョンで修正される見込み
say "${^CAPTURE}[0]"; # [0]

同様に,名前付きキャプチャについては@-@+に対応する特殊変数%-%+を駆使する代わりに,Perl 5.26で新設された%{^CAPTURE}%{^CAPTURE_ALL}という特殊変数を使うと同じようなことができます。

use 5.026;
my $string = "a: b";
$string =~ /\A(?<A>.+?): (?<B>.+?)(?: (?<C>.+))?\z/;
say scalar %{^CAPTURE}; # 2
say join ", ", sort keys %{^CAPTURE}; # A, B
say ${^CAPTURE}{A}; # a
注8)
これらの特殊変数はPerl 5.6で追加されたものです。

著者プロフィール

石垣憲一(いしがきけんいち)

翻訳家兼プログラマ。歴史ネタ担当。最近は主にCPANツールチェーン界隈の片隅で活動中。

URL:http://d.hatena.ne.jp/charsbar/

コメント

コメントの記入