これはMoritz Lenz氏のWebサイトPerlgeek.deで公開されているブログ記事"Perl 5 to 6" Lesson 18 - Scopingの日本語訳です。
原文はCreative Commons Attribution 3.0 Germanyに基づいて公開されています。
本エントリにはCreative Commons Attribution 3.0 Unportedを適用します。
Original text: Copyright© 2008-2010 Moritz Lenz
Japanese translation: Copyright© 2011 SATOH Koichi
NAME
"Perl 5 to 6" Lesson 18 - Scoping
SYNOPSIS
for 1 .. 10 -> $a {
# ここは$aが見える
}
# ここは$aが見えない
while my $b = get_stuff() {
# ここは$bが見える
}
# ここでも$bが見える
my $c = 5;
{
my $c = $c;
# ここで$cがundefになる
}
# ここでは$cは5
my $y;
my $x = $y + 2 while $y = calc();
# まだ$xが見える
DESCRIPTION
字句的スコープ
Perl6のスコープはPerl5に非常によく似ています。ブロックは新しい字句的スコープを導入します。
変数名は最も内側の字句的スコープから探索され、もし見つからなければ一つ外側のスコープを、といった手順で探索されます。
Perl5と同様にmy
変数は完全な字句的スコープ変数であり、our
宣言はパッケージ変数に字句的スコープを持った別名を作ります。
ただしちょっとした違いがあります: 変数はブロックの宣言された位置より後で可視であり、ブロックのヘッダ(例えばwhile
ループの条件節など)で宣言された変数はブロック内に限定されません。
スコープを限定したいときはブロックの形式的パラメータが使えます:
if calc() -> $result {
# ここでは$resultが使える
}
# ここでは$resultが見えない
変数は宣言の直後から可視であり、Perl5のように文の終わりからではありません。
my $x = .... ;
^^^^^
Perl6では$xは可視だが、Perl5では不可視
動的スコープ
形容詞local
はtemp
という名前になり、初期値が与えられないときは(undef
ではなく)以前の値を使うようになりました。
また不確定(hypothetical)変数と呼ばれる新しい動的スコープ変数もあります。 これは例外によってブロックから抜けた場合には以前の値を復元し、そうでない場合には値を保持します。
コンテキスト変数
Perl5でグローバルだったいくつかの変数($!
、$_
)はPerl6ではコンテキスト変数になりました。
これは動的スコープ間で受け渡しされます。
これは昔からあるPerl5の問題を解決します。Perl5ではブロックを抜ける際にDESTROY
サブルーチンが呼ばれることがあり、意図せずしてグローバル変数の値を変更してしまうことがありました。例えばエラー変数です:
# 壊れたPerl5コード
sub DESTROY { eval { 1 }; }
eval {
my $x = bless {};
die "Death\n";
};
print $@ if $@; # 何も出力されない
Perl6ではグローバル変数の暗黙的な使用がないのでこの問題は回避されます。
(Perl5.14では$@
を変更から保護しようとしており、この例にある問題のほとんどは回避されます)
疑似パッケージ
変数が同名の字句的スコープ変数で隠されているときは、OUTER
疑似パッケージを使ってアクセスすることができます
my $x = 3;
{
my $x = 10;
say $x; # 10
say $OUTER::x; # 3
say OUTER::<$x> # 3
}
同様に関数もCALLER
やCONTEXT
疑似パッケージから呼び出し元の変数にアクセスすることができます。
これらの違いはCALLER
が直前の呼び出し元の変数のみにアクセスするのに対して、CONTEXT
はUNIXの環境変数のようにはたらきます(コンパイラの内部で$_
や$!
のような変数を扱うためにだけ使うべきです)。
外部の動的スコープから変数にアクセスするためには、変数がis context
として宣言されていなければなりません。
MOTIVATION
グローバル変数が本当に悪であり、幾多の問題の温床であることは今日では広く知られています。
我々はより良いスコープ機構を実装するためのリソースを持っているので、グローバル変数は本質的にグローバルであるデータ(%*ENV
や$*PID
のような)にのみ使われています。
ブロックスコープの規則は非常に単純化されました。
Perl5のperlsyn
ドキュメントを引用しておきます; Perl6で同じことは望みません:
注意: 条件ないしループ文修飾子で修飾された"my"文(例えば"my $x if ...")の挙動は
未定義である。"my"変数の値はundefかも知れないし、以前に代入された値であったり、
あるいはそれ以外の何かかも知れない。挙動を信用してはいけない。将来のバージョンのPerl
では今使っているPerlと異なった挙動を示すかも知れない。ここには竜が潜んでいる。
SEE ALSO
S04にブロックスコープの議論があります: http://perlcabal.org/syn/S04.html
S02に疑似パッケージの全リストとコンテキストスコープの説明があります: http://perlcabal.org/syn/S02.html#Names
コメント
コメントを投稿