Seaside Laboratory

Post

Perl 特有の言語仕様メモ

Perl はマニュアルを斜め読みしただけでは理解できない癖の強い言語仕様が多いので、久しぶりに使うと書き方を忘れていたり、うまく動作しないことが多い。ハマる度に調べなおすのも面倒なのでメモしておく。

間接オブジェクト構文

間接オブジェクト構文は、クラス (パッケージ) を関数のように呼び出すための構文。

METHOD INVOCANT

と記述した場合は、

INVOCANT->METHOD

として展開される。

よく使われる print STDOUT というコードもこの仕組みを利用しているので、

# print STDOUT 'Foo'
STDOUT->print( 'Foo' ); 

なんて書き方をしても動いてしまう。

この構文を利用することで C++ 風のインスタンス生成を真似することもできる。

my $foo = new Foo();

他の言語でもおなじみの見慣れた記述にはなるが、結局のところは Foo パッケージの new 関数を呼び出しているに過ぎない。ちなみに new が使われるのは慣習のようなもので、言語の決まりではない。

コンストラクタは好きなように名付けても構いませんが、
ほとんどの Perl プログラマはコンストラクタを、new() と呼ぶのを好むようです。
ですが、new() は予約語ではありませんし、
クラスにはそういったものを供給する義務もありません。
コンストラクタとして、クラスと同じ名前の関数を使うプログラマにがいることも知られています。

少し話がそれてしまったが、この間接オブジェクト構文の利用には注意が必要である。

例を挙げると、new というメソッドの間接表記での呼び出しは、
カレントのスコープですでに new 関数が あった場合には
間違ったサブルーチン呼び出しにコンパイルされてしまいます。
結果として、望んでいたクラスメソッドではなく、
カレントパッケージの new がサブルーチンとして呼び出されることになるのです。
コンパイラはこの問題を裸の単語の require を覚えておくことによって避けようと試みますが、
それが失敗してしまった場合にはデバッグするのがとても面倒な結果となってしまうことになるでしょう。

例えば、クラスでコンポジションを使う場合、書き方によってはエラーになる。

package Foo;

sub new
{
    # Foo::new
}

sub create_bar
{
    new Bar();
}

package Bar;

sub new
{
    # Bar::new
}

package main;

Foo::create_bar();

プログラムを実行すると、

Undefined subroutine &Foo::Bar called at test.pl line 10.

Bar パッケージを new するつもりが、Bar という関数の呼び出しとして解釈されてしまいエラーになっている。マニュアルに、

-> 記法はこれらの物騒なあいまいさのどちらの影響も受けないので、
これだけを使うことを勧めます。

と書かれているように、"new Bar()" を "Bar->new()" と書き変えれば動作するようになる。

定数

定数ごときに Tips なんて必要なのかと思ってしまうが、Perl の実装は少々特殊なようである。

以下は連想配列のキーに定数を使い、キーの一覧を表示するという単純なプログラム。

use constant
{
    FOO => 1
};

my %names = ( FOO => 'foo' );

print keys( %names );

定数値の 1 が表示されるのを期待してしまうが、実際に表示されるのは "FOO" という文字列。文脈の関係なのか定数の展開が行われていない。

現在の実装では、スカラ定数は実際にはインライン化可能なサブルーチンです。
Perl バージョン 5.004 から、対応するスカラ定数は
サブルーチン呼び出しの場所に直接挿入されるので、
サブルーチン呼び出しのオーバーヘッドがなくなります。

文字列キーとして認識されてしまうのが原因なので、"FOO" を "FOO()" に書き換えればサブルーチンとして認識され、意図した動作になる。

パッケージ変数

クラス内の静的な変数 (パッケージ変数) の宣言方法は、

package Foo;
$Foo::bar = 1;

のように完全修飾した形式で変数を定義するか、

package Foo;
our $bar = 1; # $Foo::bar = 1

our を使う方法がある。Perl 5.6 以降であれば毎回パッケージ名を書く必要がない our を使った方が良い。

静的な変数はクラスの設定 (コンフィグ) 値を保存する場所として使われることがあるが、use warnings が宣言されていると面倒なことになる。例えば先程の Foo パッケージを require して使う場合。

use warnings;
require 'Foo.pm'; # Foo パッケージの読み込み

# 設定値を 2 に変更
$Foo::bar = 2;

これを実行すると、

Name "Foo::bar" used only once: possible typo at test.pl line 5.

未使用変数として警告される。確かにメインプログラム内では一度しか使われていないが、Foo パッケージ内で使われる変数なので警告されても困ってしまう。use warnings を外せば警告は表示されなくなるが、関係ない部分の警告までなくなってしまうのはまずい。

そこで、perldoc の perllexwarn に書かれているテクニックを使う。

use warnings;
my @a;
{
    no warnings;
    my $b = @a[0];
}
my $c = @a[0];

$b の代入箇所だけ警告を無効にすることができる。