スコープ

for文でループ毎にスコープが生成されないのを忘れてループ内でループ変数を参照するクロージャを作ろうとしてハマッた.

※本当はリスト内包表現などを使うべきだが,元のコードはもっと長くて見づらくなってしまうのでこういう書き方をしていた.あとリスト内包表現でも今回の問題は起こる.

>>> xs = []
>>> for x in range(10):
	xs.append(x)

>>> xs
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

だけど,

>>> xs = []
>>> for i in range(10):
	xs.append(lambda: i)

>>> [f() for f in xs]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]

こうなる.

Schemeとか使っているせいだろうか.
ちなみにC#はfor文の中はスコープ生成されますが,ループ変数はループの外側のスコープに定義されるという間を取ったような(?)仕様です.
この辺の扱いは他の言語も調べてみると面白いのかもしれない.

こうすると毎回外側のlambdaのコールフレームでyにxの値が束縛されるので回避できる.

>>> xs = []
>>> for x in range(10):
	xs.append((lambda y: lambda: y)(x))

>>> [f() for f in xs]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Python3000ではリスト内包表現内の変数がローカルになるらしいのだが,こっちはこのままだった気がする.