From: "Hisashi Yahata" <yahatah / agr.kyushu-u.ac.jp>
Subject: [ruby-list:40526] procの動作について(質問)
Date: Thu, 13 Jan 2005 16:38:47 +0900

るびきちです。

> しかし、通常のサブルーチンのようにどこに記述してもよいのではないことがわかり
> 始め、さらに単純化すれば、次の二つのように、動作が異なることに気付きました。
> Procの動作をどのように考えればよいのかどなたか教えてください。よろしくお願い
> します。

Rubyの変数はデフォルトではローカル変数なので、
「スコープ」という問題があります。変数名の先頭が
$ならグローバル変数、
@ならインスタンス変数、
@@ならクラス変数です。
また、大文字で始めると定数になります。


以下リファレンスマニュアルからの引用。

ローカル変数
Local Variable

  ある範囲内でのみ参照可能な変数。その範囲をスコープと呼ぶ。 Rubyのス
  コープは

  □ プログラム全体
  □ クラス・モジュール定義
  □ メソッド定義
  □ ブロック

  で、『ブロックだけは外側のスコープのローカル変数もアクセスできる。』ロー
  カル変数の有効範囲はスコープでの最初の代入が現れた場所からスコープの
  終りまでである。有効範囲は静的に決まり、実際に実行されるかどうかは関
  係ない。


> 問題点: (A)では、beginでaを定義し、prで内容を変換したつもりでも、変換さ
> れない。
>       しかし、(B)では、prによってaの内容を3に変更される。
これもスコープで説明できます。

> #====(A)========
> pr=proc{ a=2 }   # ブロックローカルのa(別物!)
> begin
>  a=1             # ここでローカル変数aを初期化。aのスコープが始まる
>  pr.call         # ブロックローカルのaは別物なので影響なし
> end
> pr2=proc{ p a }  # このaはローカル変数のa
> pr2.call  #=>1
pr=の時点でまだローカル変数aは定義されていません。
procブロックの中のaは「ダイナミックローカル変数」でブロック内のみ有効な変数です。
そのため、a=1のaとは別物です。


これもリファレンスマニュアルより引用すると・・・

ダイナミックローカル変数
Dynamic Local Variable

  ローカル変数の一種。Rubyのローカル変数はスコープが静的に決まるためコ
  ンパイル時に変数が作成されるが、ダイナミックローカル変数は、実行の都
  度変数が作成される。『ブロックの中で初めて代入されたローカル変数はダイ
  ナミックローカル変数となり、そのスコープはブロックの中だけとなる。』こ
  れは、Thread 毎に独立した変数を持つためにある。

(A)のスクリプトで期待した動作をさせたいならば、
先頭(pr=の前)に
a=0
などと適当な値を入れて初期化しましょう。
こうしておけば、aのスコープは最初から最後まで続いています。
スコープの法則の
『ブロックだけは外側のスコープのローカル変数もアクセスできる。』
というのがポイントです。

a=0             # ローカル変数aを初期化。aのスコープが始まる。
pr=proc{ a=2 }  # このaはローカル変数のa
                # でも実行していないのでaの現在の値は0
begin
 a=1
 pr.call        # ここでaの値は2に更新される
end
pr2=proc{ p a } # ここでもaは2
pr2.call  #=>2

> #====(B)=========
> a=1
> pr=proc{ a=3 }
> pr.call
> pr2=proc{ p a }
> pr2.call  #=>3
この場合はローカル変数aのスコープが最初から続いているので、
proc内のaもこのローカル変数となっています。


procは外側のローカル変数にもアクセスできるのがメソッド(関数)との大き
な違いです!

るびきち☆
http://www.rubyist.net/~rubikitch/ ←Ruby大衆化計画@移転