「lexical closure」の違いじゃなくてブロック「block scope」有無の違いだった

スクリプト言語間における「lexical closure」の違い』の整理。
http://d.hatena.ne.jp/karasuyamatengu/20101008/1286568265
http://d.hatena.ne.jp/karasuyamatengu/20101007/1286477371



matz 2010/10/09 16:07
えーとですね。まだ、言語ごとにやってることが違うんですよ。
つまり、PerlRubyはブロック内だけで毎回新しい変数localvarを作ってる(の
で、closure間で変数を共有していない)のに対して、Python(関数レベルしかス
コープがない)などはもっと広いスコープでlocalvarが有効でclosure間で同じ
変数を共有しています。localvarのスコープがループの中だけの言語
(Ruby,Perl)とclosuresとlocalvarが同じスコープの言語(Python,JavaScript)
というクロージャとは関係ない部分の違いです。

これは言い換ると言語がブロックスコープを持っているかいないかの違いのようだ。
JavaScriptのマニュアルによると:

JavaScriptはブロックスコープを持たない。ブロック内で宣言された変数はそれを含む関数かスクリプトのスコープに属し、その値の代入はブロック外でも有効になる。つまり、ブロックはスコープを伴わない。

CやPerlと違って、ifやforブロックは別のスコープを作らず、ブロックの中に出てくる変数は関数のスコープに住むことになる。つまり、ここ(http://d.hatena.ne.jp/karasuyamatengu/20101008/1286568265)における挙動の違いは言語がブロックスコープを持っているかいないかの違いということになるようだ。関数{ループ{変数}}のコードで変数のスコープがループなら違う値が残り、変数のスコープが関数レベルならclosure間で同じインスタンスを共有するということだろう。

JavaScriptブロックスコープの但書はタマタマ読んでいてみつけたが各言語でマニュアルから同等の部分を探すのが大変なので、テストしてみた。結果は今時の殆んどのスクリプト言語ではブロックスコープを使っていないようだ。古株のPerlだけが例外となった。Rubyの場合 .each{} はスコープ有り、for i inが無しってことかな。(Rubyの場合「ブロック」に特定の意味があるのでややっこしくなる。ここで言っているブロックはforループやif文の中のコードの塊、という意味)

以下、ブロックスコープの有無のテスト。簡略化のためループをif文に置き換えた。"oh hai"が出力されれば(と言うかコンンパイルエラーでなければ)ブロックスコープ無し。「closure比較」(http://d.hatena.ne.jp/karasuyamatengu/20101008/1286568265)において同じ値になる言語とと一致していると思う。 Sqeakがどういうことになっているのか気になる…

Python


def foo():
if True:
v="oh hai"
print v

python scope.py
oh hai

Perl


sub foo {
if (1) {
my $var="oh hai"
}
print $var, "\n";
}

perl scope.pl
Global symbol "$var" requires explicit package name at scope.pl line 9.
Execution of scope.pl aborted due to compilation errors.

Ruby


def foo
if true then
v="oh hai"
end
print v, "\n"
end

ruby1.9 scope.rb
oh hai

JavaScript


function foo() {
if (true) {
var v="oh hai";
}
print(v);
}

js scope.js
oh hai

Lua


function foo()
if 1 then
v="oh hai"
end
print(v)
end

lua5.1 scope.lu
oh hai