(Python|Ruby)でネストしたスコープの外側の変数に再束縛する
抜粋翻訳 PEP3104: Access to Names in Outer Scopesの内容をコードで。
Rubyでの挙動(1.9.3dev)
メソッドの中で定義されたメソッドは外のスコープにアクセス出来ない。
> def foo(x) > def bar() > p x > end > bar > end => nil > foo 1 NameError: undefined local variable or method `x' for main:Object from (irb):32:in `bar' from (irb):34:in `foo' from (irb):36 from /usr/local/bin/irb:12:in `<main>'
メソッドの中で定義されたブロックでは、その定義されたスコープに存在しない名前への束縛のみブロックのローカル変数とし、それ以外は外の変数への再束縛にする。
> def foo(x) > lambda {x = 1; y = 2}.call > p x > p y > end => nil > foo 100 1 NameError: undefined local variable or method `y' for main:Object from (irb):27:in `foo' from (irb):29 from /usr/local/bin/irb:12:in `<main>'
ささださんに教えてもらった: Matzにっき(2010-06-16): Ruby2.0の新機能(かもしれないもの)(2) nested def as local function
追記: セミコロンを前置することでローカル変数に強制することが出来る。Ruby Freaks Lounge:第3回 Ruby1.9の新機能ひとめぐり(中編):洗練された文法と意味論|gihyo.jp … 技術評論社
> def foo(x) > lambda {|;x| x = 1}.call > p x > end => nil irb(main):014:0> foo 100 100 => 100
Pythonでの挙動(3.0)
関数の中で定義された関数は、外の値を参照することはできるが、
>>> def foo(x): ... def bar(): ... print(x) ... bar() ... >>> foo(100) 100
再束縛をする場合ローカル変数となるので外側の値を参照することができなくなる。
>>> def foo(x): ... def bar(): ... x *= 2 ... bar() ... print(x) ... >>> foo(100) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in foo File "<stdin>", line 3, in bar UnboundLocalError: local variable 'x' referenced before assignment
この挙動をやめさせる(再束縛するxを外の変数だとみなさせる)にはnonlocal宣言を使う
>>> def foo(x): ... def bar(): ... nonlocal x ... x *= 2 ... bar() ... print(x) ... >>> foo(100) 200
JavaScriptでの挙動(1.8、というかFirefox3)
JavaScript、Perlなどその他多くの言語ではローカル変数をつくるのに宣言が必要である。なので宣言がなければ外のスコープの名前にアクセスする。
function foo(x){ function bar(){ x *= 2; } bar(); console.log(x); } foo(100); // -> 200
function foo(x){ function bar(){ var x = 2; } bar(); console.log(x); } foo(100); // -> 100