2011年2月27日日曜日

Perl 5 to 6 - スコープ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

これは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では不可視

動的スコープ

形容詞localtempという名前になり、初期値が与えられないときは(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
}

同様に関数もCALLERCONTEXT疑似パッケージから呼び出し元の変数にアクセスすることができます。 これらの違いは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

0 コメント:

コメントを投稿