Posted on 2010-07-09 23:52
dennis 阅读(1718)
评论(4) 编辑 收藏 所属分类:
动态语言
来源:
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都采用列表推断。