Perlの勘所2 スコープ

今回は変数・スコープ周りについて説明する。

変数のスコープを理解する

Perl言語の変数のスコープには,myとourとlocalの3つがある。基本的に変数のスコープにはmyを使用する。

myの使い方

myはレキシカルスコープと呼ばれるスコープを持つ。単語からでは、どういうスコープを持つのか想像しにくいのが厄介だが,他のサイトでは以下のように説明されていた。

myは、レキシカルスコープで、他言語で言えば、ブロック内のローカル変数のように振舞います。
構文上、ブロックを抜けると、宣言した変数のスコープが終了します。
宣言後、スコープから抜けるまで、他の関数やブロックから、その変数は見えません。

以下にmyの使い方をコードで示す。

use strict;
use warnings;
package AAA;
my $hoge = 'サブルーチンの外'; #変数をmyで宣言
print $hoge; #「サブルーチンの外」が出力される
&example; #exampleサブルーチン呼び出し
sub example {
    my $fuga = 'サブルーチンの中; #変数をmyで宣言
    print $hoge; #「サブルーチンの外」が出力される
    print $fuga; #「サブルーチンの中」が出力される
}

print $hoge; #「サブルーチンの外」が出力される
print $fuga; #←ここで変数を参照するとエラー

package BBB;
print $hoge; #「サブルーチンの外」が出力される

myは,最近のプログラミング言語(C,Java,PHPなど)で実装されている変数のスコープとほとんど同じである。しかし,上記の通り,同一のファイルに複数のパッケージが記載されている場合はmyはパッケージスコープを超えてしまう。これを防ぐには以下のようにパッケージに{}ブロックを付けて記述する。このようにすることでmyのスコープをパッケージ内に限定することができる。

use strict;
use warnings;
package AAA;
{
    my $hoge = 'サブルーチンの外';
    print $hoge; #「サブルーチンの外」が出力される
}

package BBB;
{
    print $hoge; #←ここで変数を参照するとエラー
}

ourの使い方

ourはレキシカルスコープを持つ。myとの大きな違いとして,以下の2点がある。

  1. スコープを抜けて,もう一度同じスコープに入ってきたときに前の値を保持している
  2. パッケージ名前空間を利用できる

以下にourの使い方をコードで示す。

use strict;
use warnings;
package foo;

my $hoge = 'サブルーチンの外';
print $hoge; #「サブルーチンの外」が出力される
&example; #1度目のサブルーチン実行 print文で「サブルーチンの中」が出力される

sub example {
    our $hoge;
    BEGIN { #BEGINは一度目の呼び出しに限り実行される
        $hoge = 'サブルーチンの中';
    }
    
    print $hoge;
}

print $hoge; #「サブルーチンの外」が出力される
$hoge = 'サブルーチンの外';
&example; #2度目のサブルーチン実行 print文で「サブルーチンの中」が出力される

上記では,一度サブルーチンのスコープを抜けて,もう一度同じサブルーチンのスコープに入ってきたときに前の値を保持している。ourはこのような特徴を持つ。

更にourでは,$foo::hogeのようにパッケージ名前空間を利用して変数を参照することができる。

package foo;
{
    our $hoge = 'foo';
}

package bar;
{
    print $foo::hoge; #パッケージ名前空間を利用して参照することができる
}

localの使い方

localは,ダイナミックスコープを持つ。ダイナミックスコープとは,文字通り動的なスコープのことで,変数のスコープがその宣言を含むブロックに限定されないことを指す。localは,サブルーチンのスコープ内でのみ有効な値を定義することができ,localを宣言したサブルーチンから呼び出したサブルーチンでも同じ値が共有される。
 
以下にlocalの使い方をコードで示す。

use strict;
use warnings;
$var1 = 'サブルーチンの外';
print $var1; #「サブルーチンの外」が出力される
&example1; #サブルーチン実行

sub example1 {
    local $var1 = 'サブルーチンの中'; #localはサブルーチン内でのみ有効
    print $var1; #「サブルーチンの中」が出力される
    &example2; #サブルーチン実行
}

print $var1; #「サブルーチンの外」が出力される

sub example2 {
    print $var1; #「サブルーチンの中」が出力される
}

上記のように,localはサブルーチンを跨いだ呼び出しを行った場合,値が共有される。また,localを使用することでグローバル変数とローカル変数を視覚的に区別することが容易になり,クラス内の見通しが良くなるという恩恵も得られる。

まとめ

変数宣言には基本的にmyを使用する。グローバル変数的に使用したい変数にはourを使用する。localはサブルーチンの中でのみ使用することができるが,myでも代替することは可能である。ourやlocalを使用することに抵抗がある場合は,無理して使わなくてもよい。Perlのスローガンに「やり方はひとつじゃない」とある通り,ユーザはPerlに用意された様々な方法の中から,自分に合った方法を選ぶことが出来る。

参考
my と local どう違う? – futomi’s CGI Cafe
Perlのour – Perlのourについて。