闭包使用的一个陷阱

来源: 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都采用列表推断。

文章转自庄周梦蝶  ,原文发布时间 2010-07-09
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值