my と local どう違う?

ここでは、変数の局所化に使う my と local について解説します。どちらも変数を局所化することには違いないのですが、細かな点で違いがあり、これを理解していないと、想定しない結果となってしまいます。しっかりと押さえておきたいポイントです。

my について

ここでは、my について解説します。my は、Per5から実装された関数で、Perl4では使えません。Perl4では local のみが使えましたが、Perl5では、my を使うのが一般的ですので、しっかりと理解してください。

まず、言葉を覚えましょう。my を使って定義された変数は、レキシカルスコープ変数 と呼びます。そして、my を使って変数を宣言することをレキシカルスコープ宣言と呼びます。

宣言の仕方は

my $var; #宣言のみ
my $var = 'hello'; #宣言と同時に値も同時に格納
my($foo, $var); #複数の変数名を同時に宣言

などです。では、レキシカルスコープ変数の有効範囲は、どこまでかと言うと、次の例を見てください。

1
2
3
4
5
6
7
8
$var = 'サブルーチンの外';
print $var; #「サブルーチンの外」が出力される
&example;
sub example {
    my $var = 'サブルーチンの中';
    print $var; #「サブルーチンの中」が出力される
}
print $var; #「サブルーチンの外」が出力される

この場合、2行目で出力される値は「サブルーチンの外」ですね。そして6行目で出力される値は「サブルーチンの中」となります。サブルーチンの中で、$var のレキシカルスコープ宣言をしているため、新たにメモリー上に値が保持されることになります。もちろん、2行目で定義した $var の値も保持されたままです。それぞれの $var は、同じ名前にもかかわらず、変数名も値も、別々に保持されます。つまり、それぞれの $var は、名前は同じですが、まったく別の変数として処理されます。ただし、5行目のサブルーチン内で定義された $var の変数名と値は、サブルーチンを抜けた時点で、破棄されてしまいます。

では、8行目では、何か出力されるのでしょうか。上の説明でお分かりかと思いますが、「サブルーチンの外」が出力されるわけです。サブルーチンの外からは、5行目で定義した値は参照できないことに注意してください。

次に、5行目で my を使って宣言しなかった場合には、どうなるでしょうか。

1
2
3
4
5
6
7
8
$var = 'サブルーチンの外';
print $var; #「サブルーチンの外」が出力される
&example;
sub example {
    $var = 'サブルーチンの中';
    print $var; #「サブルーチンの中」が出力される
}
print $var; #「サブルーチンの中」が出力される

上記の通り、8行目の値が変わってしまいましたね。5行目で my を使って局所化していなかったため、1行目で代入した値が、5行目で書き換わってしまったのです。そのため、8行目の出力結果も変わってしまったというわけです。意図的にそうしたいのであれば問題ないのですが、そうでなかったら、バグになってしまいますね。

次に、サブルーチンがネスト(入れ子)になったパターンを紹介します。ここで言っているネストとは、サブルーチンの中でサブルーチンを呼び出した場合のことを指しています。

1
2
3
4
5
6
7
8
9
10
11
$var = 'サブルーチンの外';
&example;
sub example {
    my $var = 'サブルーチンの中';
    print $var; #「サブルーチンの中」が出力される
    &example2;
}
sub example2 {
    print $var; #「サブルーチンの外」が出力される
}
print $var; #「サブルーチンの外」が出力される

この例では、4行目でレキシカルスコープ変数として $var を宣言していますので、この $var の有効範囲は、7行目までとなります。しかし、その手前の6行目でサブルーチン example2 を呼び出していますね。その場合、4行目で宣言された $var は example2 の中でも有効になるのでしょうか。答えは、Noです。9行目で呼び出している $var は、グローバル変数の $var で、1行目で宣言されたものなんです。

話が若干それますが、もし、変数をサブルーチンに引き継ぎたい場合には、以下の例のように、引数をを正しく与えてるべきです。

1
2
3
4
5
6
7
8
9
10
11
12
$var = 'サブルーチンの外';
&example;
sub example {
    my $var = 'サブルーチンの中';
    print $var; #「サブルーチンの中」が出力される
    &example2($var);
}
sub example2 {
    my($var) = @_;
    print $var; #「サブルーチンの中」が出力される
}
print $var; #「サブルーチンの外」が出力される