【Perl】my と our と local と……


当たり前のことなのだけど、改めて纏めてみる。レキシカルスコープとパッケージの範囲って全然違うんだなあ。

ourmy

#!/usr/bin/perl
use strict;
use feature qw! say !;

package AAA;
our $our_var = 123;
my $my_var = 567;

package DDD;
say '$our_var      : ' . ( $our_var      // "undefined" );
say '$AAA::our_var : ' . ( $AAA::our_var // "undefined" );
say '$DDD::our_var : ' . ( $DDD::our_var // "undefined" );
say '$my_var       : ' . ( $my_var       // "undefined" );
say '$AAA::my_var  : ' . ( $AAA::my_var  // "undefined" );
say '$DDD::my_var  : ' . ( $DDD::my_var  // "undefined" );
$our_var      : 123
$AAA::our_var : 123
$DDD::our_var : undefined
$my_var       : 567
$AAA::my_var  : undefined
$DDD::my_var  : undefined
  1. ourmy もレキシカルスコープ全体で有効になる。この例の場合、その範囲はスクリプト全体となる。
  2. レキシカルスコープ内では例えパッケージが違っていても参照できる(10, 13 行目)。
  3. our は変数をパッケージの名前空間に紐付けする。つまり、グローバル変数を作るmy にそのような効果はないのでパッケージ名を付けても参照できない(14 行目)。

ouruse vars

our とよく似た機能として use vars プラグマがある。どちらも変数をパッケージの名前空間に紐付けする点では同じだが、ourレキシカルスコープ内で有効になるのに対して、use varsパッケージ内で有効になる点が異なる。

#!/usr/bin/perl
use strict;
use feature qw! say !;

package AAA;
use vars qw! $use_vars_var !;
$use_vars_var = 678;
our $our_var = 123;

package DDD;
say $our_var;           # これは大丈夫
say $AAA::use_vars_var; # これも大丈夫
say $use_vars_var;      # ここでエラー!
say $use_vars_var;      # ここでエラー!

use vars が無効な範囲でもパッケージ名で修飾すれば参照できる(12 行目)。歴史の古いモジュールなどではよく見かけるが、our の使える今では、あえて使う意味はない。

locallocal our

local はグローバル変数にスコープ内でのみ有効な値を与えるときに使われる。

#!/usr/bin/perl
use strict;
use feature qw! say !;

our $var = 123;

{
    local $var = 345;
    &say_var;
}

&say_var;

sub say_var {
    say $main::var // "undefined";
}
345
123

local 文で与えた変更はスコープ内で呼び出された関数でも有効だ。では次のコードだとどうなるか。

#!/usr/bin/perl
use strict;
use feature qw! say !;

{
    our $var = 345;
    &say_var;
}

&say_var;

sub say_var {
    say $main::var // "undefined";
}
345
345

our が有効なのはスコープ内だけであるのに、そこで与えた変更はスコープ外にも波及する。これを避けるために local our を使う。

#!/usr/bin/perl
use strict;
use feature qw! say !;

{
    local our $var = 345;
    &say_var;
}

&say_var;

sub say_var {
    say $main::var // "undefined";
}
345
undefined

これでスコープ内だけで有効なグローバル変数が使えるようになった。一時的に、定数に別の値を与えて関数を呼び出す場合に有効だ。

でも結局……

しかしこのような例は Perl においてさえもレガシーといえる書き方だ。関数の挙動を決めるのにはグローバル定数なんか使わず、素直に引数を与えて呼び出した方がよい。

#!/usr/bin/perl
use strict;
use feature qw! say !;

my $var = 678;

{
    my $var = 345;
    &say_var( $var );
}

&say_var( $var );

sub say_var {
    my $var = shift;
    say $var // "undefined";
}
345
678

これだけ書いておいて何だが、結局言えるのは「グローバル変数はトラブルの元。全部 my 使ってレキシカル変数で処理した方がマシ」ってこと。他人のコード読むときは仕方ないけどね。

コメントを残す