来源:
http://moonbase.rydia.net/mental/blog/programming/the-biggest-mistake-everyone-makes-with-closures.html
看下面的Ruby代码
k
=
[]
for
x
in
1
..
3
k.push(
lambda
{ x }) end
执行
k[0].call
你可能预期返回1,实际的结果却是3。这是为何?这是因为在
迭代过程中共用了同一个context,导致k中的
三个闭包都引用了同一个变量x。不仅仅Ruby有这个问题,python也一样
k
=
[
lambda
: x
for
x
in
xrange(
1
,
4
)] k[0]()
Javascript同样如此
var k
=
[];
for
(var x
=
1
; x
<
4
; x
++
) { k.push(function () {
return
x; }); } alert(k[0]())
解决这个问题很简单,就是将
闭包包装到一个函数里,建立新的context,那么迭代过程中生成的闭包所处的context不同:
def
make_value_func(value)
lambda
{ value } end k
=
(
1
..
3
).map {
|
x
|
make_value_func(x) }
这个时候,k[0].call正确地返回1。
这个问题并非在所有支持闭包的语言里都存在,例如scheme中就没有问题
(define k
'
())
(do ((x
1
(
+
x
1
))) ((
=
x
4
)
'
())
(set! k (cons (
lambda
() x) k))) (set! k (reverse k)) ((car k))
=>
1
Erlang也没有问题
K
=
[ fun()
->
X end
||
X
<-
[
1
,
2
,
3
]]. lists:map(fun(F)
->
F() end,K).
再试试Clojure:
(def k (
for
[i (range
1
4
)] (fn [] i))) (map #(
%
) k)
同样没有问题。这里Erlang和Clojure都采用列表推断。
文章转自庄周梦蝶 ,原文发布时间 2010-07-09
相关资源:python闭包与引用以及需要注意的陷阱