Posted on 2010-07-09 23:52
dennis 閱讀(1729)
評論(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都采用列表推斷。