Perl Hackers Hub

第14回 最新Perl使いこなし術―リファレンスの引き方,5.10以降の新機能(2)

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

最近のPerlの機能

リファレンスの引き方がわかったところで,最近のPerlに追加された機能を紹介します。Perlは枯れた言語というイメージがある人もいると思いますが,まだまだ活発に開発が続けられており,バージョンが上がるたびに便利な機能が増えています。

Perlの安定バージョンについて

perlの安定版はバージョン番号X.Y.ZのマイナーバージョンYが偶数のものです。最近の安定版は5.8(2002年7 月リリース)⁠5.10(2007 年12 月リリース)⁠5.12(2010 年4 月リリース)⁠5.14(2011 年5 月リリース)です。本稿ではperl 5.10以降に実装された機能を「最近のPerlの機能」として紹介します。

perlの公式安定版として一番新しいのは5.14ですが,多くの人はまだ5.8系を使っているでしょう(非常に安定しているため)⁠しかし5.8系の最新5.8.9のリリースは2008年12月と,よく言えば枯れているとはいえ,古いものです。現在筆者は個人的には5.14.2を使っており,仕事で使っているのも5.12以降のものです。Mac OS X Lionに標準で入っているのも5.12です。

以降では最近のPerlの機能を,どこがどう便利なのか,実際にどんな感じで使うのか,あるいは実際に使えるのかを解説します。

5.10からの機能

まずは5.10からの機能です。

featureプラグマ─⁠─新機能を使うおまじない

最初はあっと驚く機能ではないけれど重要な機能の紹介です。新しい機能は,古いperlとの互換性のためデフォルトでは無効になっているものもあります。featureプラグマはそういった機能を有効にしたいときに使います。

これはプログラム冒頭でuse feature qw(say switch)のように書く「おまじない」です。

たとえばuse feature ':5.10'と書けば5.10で使える機能はすべて有効になります。use 5.10.0;use v5.10;と書いても同じです。ただし,5.12以降でuse v5.12;とすると,use feature ':5.12'に加えてuse strict;相当にもなります。筆者はuse strict効果を狙いつつ,なんとなくカッコいいuse v5.12;を好んで使っています。

use feature 'say';とすると,次のsay関数が使えるようになります。

say関数─⁠─改行付きで出力

print関数は\nを自分で付けないと改行してくれませんが,say関数は改行を付けて出力してくれます。say $string;はprint $string, "\n";と同じです。ちょっと出力したいときに改行をいちいち書かなくてよいのは,ストレスがかなり軽減されます。

say関数自体はPerlの文法に何の影響も与えていないので,5.8以下でも同じように動くモジュールとしてCPANでPerl6::Sayが提供されており,これをuseすることでも同じ関数を得られます。

defined-or演算子─⁠─undefとそれ以外を区別

defined-or演算子はオペランドを2つ($A,$B)とり,$A // $Bのように書きます。名前のとおり,defined $Aが真でなければ$Bを評価して返します。似たような演算子としてor 演算子$A || $Bがありますが,defined-or演算子はこれのundefの場合のみ$Bが評価されるバージョンと考えることができます。

次のように使います。

my $bar = $foo // 1;

$fooがundefの場合,$barには1が代入されます。

また,自己代入演算子も定義されており,ほかの似たような演算子と同じく,次のようにも使えます。

$baz->{foo} //= 'hello, world!';

$baz->{foo}がundef ならば'hello, world!'を代入する,という意味です。

この演算子は非常に重要です。Perlにおいては空の文字列の''も,文字列の'0'も,数値の0も偽になるため,or演算子では期待するよりも多くの場合で後続のオペランドが評価される場合があります。典型的な例として次のようなコードがあります。

sub title {
  my ($self) = @_;
  $self->{_title} ||= $self->retrieve_title_from_db;
}

この例では,データベースからタイトル(文字列)を取得するtitleメソッドを定義しています。titleメソッド自体は何度も呼ぶ可能性があるので,データベースの結果を$selfにキャッシュしています。

defined-or演算子を使わずにor演算子を使っているため,$self->retrieve_title_from_dbが空文字列の場合,何度も何度も$self->retrieve_title_from_dbが評価されることになってしまいます。データベースアクセスが発生する場合は特に望ましくない挙動です。defined-or演算子を使えば,$self->{_title}がundefの場合のみ$self->retrieve_title_from_dbが評価されることになり安心です。

5.8以前でこの演算子と同じことをしようとすると,次のようになります。

my $bar = defined $foo ? $foo : 1;

$baz->{foo} = defined $baz->{foo} ? $baz->{foo} : 1;

sub title {
  my ($self) = @_;
  $self->{_title} =
    defined $self->{_title} ? $self->{_title} :
    $self->retrieve_title_from_db;
}

自己代入の場合などは特に冗長になって書きにくく,可読性が下がります。このようにdefined-or演算子がない場合,条件演算子を使わなければなりませんが,面倒なためor演算子を使いがちになり,意図しない処理が走り結果的にバグにつながります。

この演算子を使うためだけでも,5.10以降を使う理由になると思います。

正規表現のnamed-capture─⁠─キャプチャの参照が楽に

Perlの正規表現では,括弧()を使うことでその部分にマッチした文字列は$1,$2,$3………という特別な変数に代入され(キャプチャと言います)⁠あとから取得できます。でも,括弧の数が増えてきたり,正規表現が複雑になってくると,どれがどの変数に入るのかわかりにくくなります。これを解決するのがnamedcaptureです。

次の例のようにただの括弧の代わりに(?<識別子>元の正規表現)という書き方をすることで,$+{識別子}$-{識別子}という書き方で参照できるようになります(%+%-というハッシュに値が入り,$+{識別子}でハッシュの値を取得するという意味です)⁠

my $string = 'http://example.com/foo/bar';
$string =~ m{^http://(?<host>[^\s/]+)(?<path>/\S+)$};

say $+{host};
say $+{path};

特に便利なのは,パイプで区切って複数の似たような表現を羅列するときでしょう。次のようにした場合,マッチしたのがどちらであれ,$+{rgb}にマッチした文字列が入ります。

$string =~ m{
  (?<rgb>\#[0-9a-f]{6}) |
  (?<rgb>\#[0-9a-f]{3})
}xi;

say $+{rgb};

同じ名前で複数の場所がキャプチャされた場合,$+{rgb}には最後にマッチした内容が入ります。全部欲しいケースでは,次のように$-{識別子}という特殊な変数を使います。$-{識別子}には配列リファレンスですべての値が入っています。

my $string = "aaa bbb ccc";
$string =~ m{(?<chars>...) (?<chars>...)};

$-{chars}; #=> ['aaa', 'bbb']

正規表現が複雑になるほど,この機能は便利です。どれがキャプチャの括弧なのかを見極めながら括弧の数を数える生活から解放されます。

著者プロフィール

cho45(さとう)

はてなエンジニア。サブテカ。バックエンドからインターフェイスまで,Perl,Ruby,JavaScript,ActionScriptなどを使いつつ Scala,Ioなどを触る,言語・コード表現ヲタク。

URLhttp://www.lowreal.net/
技術ネタhttp://subtech.g.hatena.ne.jp/cho45/

コメント

コメントの記入