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