変数の宣言とスコープ

C言語, C++

変数には必ず宣言が必要で、静的スコープのローカル変数になる。

Java

変数には必ず宣言が必要で、静的スコープのローカル変数になる。

ローカル変数のスコープはブロックの中で宣言より後ろの部分になる。ブロックはメソッド定義や、if文、for文などの {} の中で、入れ子になったブロックの内側で宣言した変数のスコープは内側のブロックになる。外側のブロックで宣言した変数を内側のブロックで参照・代入することはできる。同じ名前の変数を改めて内側のブロックで宣言して外側の変数を隠すことはできない。

宣言のみで代入を一度もしていない変数を参照しようとするとコンパイルエラーになる。

final を付けると2回以上代入することができない。クラスのフィールドに final を付けると、コンストラクタからのみ1回だけ代入できる。

Scala

変数には必ず宣言が必要で、静的スコープのローカル変数になる。変数宣言には valvar の2種類ある。

val を付けると、宣言と同時に代入が必要で、その後の再代入はできない。変数というよりは値に名前を付けるイメージ。

var を付けると、Javaの final の付いていない変数と同様に、再代入が可能。しかし val と同様に代入せずにの宣言のみをすることはできない。

変数宣言の例

val foo = 3; // fooの型は Int になる
val foo = Vector(1, 2, 3); // fooの型は Vector[Int] になる
val foo: Seq[Int] = Vector(1, 2, 3); // fooの型は Seq[Int] になる
val foo: Int = _; // fooの値は 0 になる
val foo: Seq[Int] = _; // fooの値は null になる

var foo = 3; // var も書き方は val と同じ
var foo: Boolean = _; // fooの値は false になる
var foo = _; // 値を決められないのでコンパイルエラー

関数の中や iffor などのブロックの中など、だいたい {} で新しいスコープが作成される。

PHP

変数の宣言というのはなく、変数への代入があるとローカル変数が作られる。

関数の中で使用している変数のスコープは関数の中で、トップレベルで使用している変数のスコープはグローバルになる。

グローバル変数を関数の中から使用するには、その関数の中で global でグローバル変数を宣言する必要がある。宣言をしておけばグローバル変数の参照も代入もでき、同じ名前のローカル変数は作られない。

global | PHP Manual
http://www.php.net/manual/ja/language.variables.scope.php#language.variables.scope.global

グローバル変数は global 宣言しなくても、 $GLOBALS という変数で参照することもできる。

$GLOBALS | PHP Manual
http://www.php.net/manual/ja/reserved.variables.globals.php

global での宣言は実際にはグローバル変数へのレファレンスをローカルに作成するのと同じである。

global リファレンス | PHP Manual
http://www.php.net/manual/ja/language.references.spot.php#references.global

スーパーグローバル変数と呼ばれるものは、 global で宣言しなくても関数の中から使える。 $GLOBALS という変数自体もスーパーグローバル変数である。

スーパーグローバル | PHP Manual
http://www.php.net/manual/ja/language.variables.superglobals.php

関数の中では新しいスコープが作成されるが、 iffor などの制御構文ではスコープが作成されないので、これらの中で変数を作成しても、その制御構文を脱した後も使える。

関数スコープの変数には静的変数(static)というものがある。 C言語の静的変数と同じで、変数の初期化は一度のみで、その後変数の値を変更すると、次の関数呼び出しでも変更後の値が生きる。初期化は最初にその関数が呼び出されたときに実行される。

Python

変数の宣言というのはなく、変数への代入があるとローカル変数が作られる。ローカル変数のスコープは関数定義やクラス定義の中になる。 if文やfor文などの制御構文でスコープが作られることはない。関数の外側のトップレベルであればグローバルスコープになる。グローバル変数は必ずその属するモジュールのモジュールオブジェクトの属性になる。

内側のスコープから外側のスコープの変数を参照することはできるが、外側の変数への代入はできない。代入しようとするとローカル変数が作成されてしまう。

ローカル変数は代入の存在するスコープで作成されるが、作成されるのは代入の時点ではなくスコープに入ったタイミングであるので、代入より前に外側の同じ名前の変数を参照しようとしても、初期化が行われていないとしてエラーが発生する。

Python 3では、nonlocal を使うことで、ローカルスコープに新たな変数を作成するのではなく、外側にある変数を使用することを宣言できる。

Ruby / JRuby

$ で始まる名前の変数がグローバル変数となる。アルファベット小文字またはアンダースコアで始まる名前の変数がローカル変数となる。グローバル変数とローカル変数と名前が衝突することはない。

変数の宣言というのはなく、変数への代入でグローバル変数やローカル変数が作られる。

メソッド定義やクラス定義の中ではなく、トップレベルでもローカル変数を使うことができるが、これはトップレベルでしか使うことができず、メソッド定義やクラス定義の中から参照することができない。

メソッド・ブロックやクラス定義では新しいスコープが作成されるが、 iffor などの制御構文ではスコープが作成されない。

メソッド定義やクラス定義では、その外側のスコープの変数を参照することはできない。ブロックの中から外側のスコープの変数を参照・代入することはできる。従って、メソッド定義やクラス定義の中から外側の変数を参照・代入したい場合は define_methodClass.new を使って、ブロックで定義するとよい。

a = 1;
# この a はローカル変数

def f1
  # p a
  # ここから外側にある a を参照することはできない

  a = 2
  # このaは関数の中のローカル変数で、外側の a とは無関係

  p a # => 2
end

f1
p a # => 1
# f1 が呼び出されていても f1 の外側の a は変更されない

$a = 10;
def f2
  p $a # => 10
  # $a はどこからでも参照できる

  $a = 20

  p $a # => 20
end

f2
p $a # => 20

Perl

宣言なしに変数を使うとグローバル変数になる。

local で宣言すると、動的スコープのローカル変数になる。

my で宣言すると、静的スコープのローカル変数になる。関数の外で my 宣言すると、そのファイルの中でグローバル変数になる。

最初のPerlはグローバル変数のみだったが、Perl 4から local (動的スコープ)が導入され、Perl 5から my (静的スコープ)が導入された。

宣言なしでの変数の使用は推奨されていない。

{} のブロックでスコープが作成される。その中で my で宣言した変数はそのブロックの中からしかアクセスできない。ブロックは入れ子にできる。

my $a = 1;
{
    my $a = 2;
    {
        my $a = 3;
        print "$a\n"; # => 3
    }
    print "$a\n"; # => 2
}
print "$a\n"; # => 1

名前が衝突していなければ、関数や {} ブロックの中からグローバル変数などの外側の変数を自由に参照・代入できる。

JavaScript

宣言なしに変数を使うとグローバル変数になる。

var で宣言すると、静的スコープのローカル変数になる。スコープの範囲は関数の中である。 if文やfor文などのブロックはスコープにならないので、その中で変数を宣言しても関数全体で使えてしまう。 for文などの繰り返し文のブロックの中で宣言しても、ループの1回りだけではなく、ループ全体を通して変数が共有されてしまう。

JavaScriptではコールバック関数を定義することが多いので、以下のようなコードでは意図した通りに動かずにハマる。

function setup() {
    var data = [ ... ]; // なんらかの配列

    for (var i = 0; i < data.length; i++) {
        var d = data[i];

        // あとで非同期で呼び出される前提のコールバック関数を生成して、どこかに渡す
        foo(i, function() {

            bar(d); // ここで d を使ったなんらかの処理

        });
    }
}

コールバック関数の中で参照されている d はループの中で宣言されているが、実際には setup 関数の全体で有効な変数であるので、ループが回るごとに d が上書きされる。コールバック関数が後で非同期で呼び出される頃には d はループの最後の要素が入っていることになり、 data.length の数だけ生成されたコールバック関数はすべて、結局 data の最後の要素しか参照できない。

d をループの中だけのローカル変数にするために以下のように関数にしてしまえば、意図した動きになる。ループの中で return, break, continue に相当する処理をしたい場合には一工夫必要である。

function setup() {
    var data = [ ... ]; // なんらかの配列

    for (var i = 0; i < data.length; i++) {
        (function() {
            var d = data[i];

            // あとで非同期で呼び出される前提のコールバック関数を生成して、どこかに渡す
            foo(i, function() {

                bar(d); // ここで d を使ったなんらかの処理

            });
        })();
    }
}
このサイトは筆者(hydrocul)の個人メモの集合です。すべてのページは永遠に未完成です。