闭包使用的一个陷阱


来源: http://moonbase.rydia.net/mental/blog/programming/the-biggest-mistake-everyone-makes-with-closures.html

看下面的Ruby代码
=  []
for  x  in   1 .. 3
  k.push(
lambda  { x })
end

执行
k[0].call

你可能预期返回1,实际的结果却是3。这是为何?这是因为在 迭代过程中共用了同一个context,导致k中的 三个闭包都引用了同一个变量x。不仅仅Ruby有这个问题,python也一样
=  [ 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
=  ( 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都采用列表推断。




你可能感兴趣的:(闭包使用的一个陷阱)