スキップしてメイン コンテンツに移動

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

コメント

このブログの人気の投稿

LIBLINEAR 2.41 で One-class SVM が使えるようになったので Perl から触ってみよう

改訂 (Sep 15, 2020): 必要のない手順を含んでいたのでサンプルコードと記述を修正しました。 CPAN に Algorithm::LibLinear 0.22 がリリースされました (しました。) 高速な線形 SVM およびロジスティック回帰による複数の機械学習アルゴリズムを実装したライブラリである LIBLINEAR への Perl バインディングです。 利用している LIBLINEAR のバージョンが LIBLINEAR 2.30 から LIBLINEAR 2.41 に上がったことで新しいソルバが追加され、One-class SVM (OC-SVM) による一値分類が利用可能になっています (しました。) OC-SVM って何 一値分類を SVM でやること。 一値分類って何 ある値が学習したクラスに含まれるか否かを決定する問題。 HBO の「シリコンバレー」に出てきた「ホットドッグ」と「ホットドッグ以外」を識別するアプリが典型。「ホットドッグ以外」の方は犬でも神でも一つの指輪でも何でも含まれるのがミソ。 二値分類の場合正反両者のデータを集める必要があるのに対して、一値分類の学習器は正例データのみしか要求しない (ものが多い。) 主な用途は外れ値検出で、もちろんホットドッグやホットドッグ様のものを検出したりもできる。 使い方 手順自体は他の二値ないし多値分類問題と同じです。つまり、 訓練パラメータを決めて 訓練データセットで訓練して テストデータセットで確度を検証して 十分良くなったらモデルを保存する といういつもの流れ。 訓練パラメータ use 5.032 ; use Algorithm::LibLinear ; my $learner = Algorithm::LibLinear ->new( epsilon => 0.01 , nu => 0.75 , solver => ' ONECLASS_SVM ' , ); solver => 'ONECLASS_SVM' が一値分類用のソルバです。LIBLINEAR の train コマンドで言うところの -s 21 。 OC-SVM の良いところは (ハイパー)...

Perl 5 to 6 - コンテキスト

2011-02-27: コメント欄で既に改訂された仕様の指摘がありました ので一部補足しました。 id:uasi に感謝します。 これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 06 - Contexts の日本語訳です。 原文は 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 06 - コンテキスト SYNOPSIS my @a = <a b c> my $x = @a; say $x[2]; # c say (~2).WHAT # Str() say +@a; # 3 if @a < 10 { say "short array"; } DESCRIPTION 次のように書いたとき、 $x = @a Perl5では $x は @a より少ない情報—— @a の要素数だけ——しか持ちません。 すべての情報を保存しておくためには明示的にリファレンスを取る必要があります: $x = \@a Perl6ではこれらは反対になります: デフォルトでは何も失うことなく、スカラ変数は配列を単に格納します。 これは一般要素コンテキスト(Perl5で scalar と呼ばれていたもの)及びより特化された数値、整数、文字列コンテキストの導入によって可能となりました。無効コンテキストとリストコンテキストは変更されていません。 特別な構文でコンテキストを強制できます。 構文 コンテキスト ~stuff 文字列 ?stuff 真理値 +stuff ...

Perl の新 class 構文を使ってみる

Perl 5 のオブジェクト指向機能は基本的には Python の影響を受けたものだが、データを名前空間 (package) に bless する機構だけで Perl 4 以来の名前空間とサブルーチンをそのままクラスとメソッドに転換し第一級のオブジェクト指向システムとした言語設計は驚嘆に価する。 実際この言語のオブジェクトシステムは動的型付言語のオブジェクト指向プログラミングに要求されるおよそあらゆる機能を暗にサポートしており、CPAN には Moose を筆頭とした屋下屋オブジェクトシステムが複数存在しているがその多くは Pure Perl ライブラリである。つまり「やろうと思えば全部手書きで実現できる」わけである。 そういうわけで Perl のオブジェクト指向プログラミングサポートは機能面では (静的型検査の不在という現代的には極めて重大な欠如を除けば) 申し分ないのだが、しかし Moose その他の存在が示しているように一つ明らかな欠点がある。記述の冗長さだ。 コンストラクタを含むあらゆるメソッドは第一引数としてレシーバを受ける単なるサブルーチンとして明示的に書く必要があるし、オブジェクトのインスタンス変数 (a.k.a. プロパティ / データメンバ) は bless されたデータに直接的ないし間接的に プログラマ定義の方法 で格納されるためアクセス手段は実装依存である。これはカプセル化の観点からは望ましい性質だが、他者の書いたクラスを継承するときに問題となる。ある日データ表現を変更した親クラスがリリースされると突然自分の書いた子クラスが実行時エラーを起こすようになるわけだ。 そうならないためにはインスタンス変数へのアクセスに (protected な) アクセサを使う必要があるのだが、そのためには親クラスが明示的にそれらを提供している必要があるし、そもそも Perl にはメソッドのアクセス修飾子というものがないので完全な制御を与えるならばオブジェクトの内部状態がすべて public になってしまう。 そのような事情もあり、特にパフォーマンスが問題にならないようなアプリケーションコードでは Moose のようなリッチな語彙を提供するオブジェクトシステムを使うことが 公式のチュートリアルでも推奨 されてきた。Perl コアのオブジェクトシステムの改良は...

Perl 5 to 6 - 正規表現(またの名をルール)

これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 07 - Regexes (also called "rules") の日本語訳です。 原文は 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 07 - 正規表現(またの名をルール) SYNOPSIS grammar URL { token TOP { <schema> '://' [<ip> | <hostname> ] [ ':' <port>]? '/' <path>? } token byte { (\d**{1..3}) <?{ $0 < 256 }> } token ip { <byte> [\. <byte> ] ** 3 } token schema { \w+ } token hostname { (\w+) ( \. \w+ )* } token port { \d+ } token path { <[ a..z A..Z 0..9 \-_.!~*'():@&=+$,/ ]>+ } } my $match = URL.parse('http:/...

去る6月に Perl 5.32.0 がリリースされたので差分を把握するために perldelta を読んだ件

要旨 Perl 5 メジャーバージョンアップの季節がやって来たのでまともな Perl プログラマの嗜みとして perldelta を読んだ。 今回は有り体に言えばルーティン的なリリースで、言語コアの拡張は他言語にも見られる構文が実験的に入ったくらいで大きな変化はない。新機能は RegExp の拡充が主である。 比較的重要と思われる変更点を抜粋する。 新機能 isa 演算子 実験的機能。Python とか Java における isinstance とか instanceof 。 これまでも UNIVERSAL::isa があったが、これはメソッドなのでレシーバにオブジェクトでもクラスでもない値 (i.e., 未定義値 / bless されていないリファレンス) を置くと実行時エラーが起きるのが問題だった: package Foo { use Moo; } package Bar { use Moo; extends ' Foo ' ; } package Baz { use Moo; } use feature qw/ say / ; sub do_something_with_foo_or_return_undef { my ( $foo ) = @_ ; # Returns safely if the argument isn't an expected instance, in mind. return unless $foo -> isa ( ' Foo ' ); ...; } # OK. do_something_with_foo(Bar->new); # |undef| is expected in mind, but actually error will be thrown. do_something_with_foo( undef ); これを避けるために今までは Scalar::Util::blessed を併用したりしていたわけだが、 isa 演算子は左辺が何であっても意味のある値を返すのでよりシンプルになる: # True +( bless +{} ...