莊周夢(mèng)蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

          閉包使用的一個(gè)陷阱

          Posted on 2010-07-09 23:52 dennis 閱讀(1735) 評(píng)論(4)  編輯  收藏 所屬分類: 動(dòng)態(tài)語言

          來源: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

          執(zhí)行
          k[0].call

          你可能預(yù)期返回1,實(shí)際的結(jié)果卻是3。這是為何?這是因?yàn)樵?strong>迭代過程中共用了同一個(gè)context,導(dǎo)致k中的三個(gè)閉包都引用了同一個(gè)變量x。不僅僅Ruby有這個(gè)問題,python也一樣
          = [lambda: x for x in xrange(14)]
          k[0]()

          Javascript同樣如此

          var k = [];
          for (var x = 1; x < 4; x++) {
            k.push(function () { 
          return x; });
          }
          alert(k[0]())


          解決這個(gè)問題很簡單,就是將閉包包裝到一個(gè)函數(shù)里,建立新的context,那么迭代過程中生成的閉包所處的context不同:
          def make_value_func(value)
            
          lambda { value }
          end
          = (1..3).map { |x| make_value_func(x) }

          這個(gè)時(shí)候,k[0].call正確地返回1。

          這個(gè)問題并非在所有支持閉包的語言里都存在,例如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都采用列表推斷。





          評(píng)論

          # re: 閉包使用的一個(gè)陷阱  回復(fù)  更多評(píng)論   

          2010-07-10 00:40 by Rain Yang
          Ruby 1.9.x 不會(huì)再這樣了。

          # re: 閉包使用的一個(gè)陷阱[未登錄]  回復(fù)  更多評(píng)論   

          2010-07-15 14:46 by sin
          actionscript同樣問題

          # re: 閉包使用的一個(gè)陷阱  回復(fù)  更多評(píng)論   

          2012-12-22 15:57 by Zhiqiang.Zhan
          這不是閉包的缺陷,這是因?yàn)镽uby,Python還有JavaScript這些支持函數(shù)式等編程語言中的block都不能形成閉包作用域,而你拿來對(duì)比的Erlang和Clojure的例子很不恰當(dāng),它的等價(jià)形式相當(dāng)于Ruby中的
          k = []
          (1..3).each do |x|
          k.push(lambda {x})
          end

          puts k[0].call

          而這個(gè)剛好是OK的。
          這是因?yàn)閐o...end block形成了閉包,但是for ...end中的語句沒有形成新的閉包。

          # re: 閉包使用的一個(gè)陷阱  回復(fù)  更多評(píng)論   

          2013-11-04 02:45 by 我傻逼我自豪
          js例:
          var a = [];
          for (var x = 4; x >= 0; x--) {
          a.push(function (x) {
          return x;
          });
          }
          alert(a[4]());
          主站蜘蛛池模板: 阿荣旗| 荣成市| 新和县| 玉门市| 乐至县| 栖霞市| 青河县| 南漳县| 兴和县| 庆云县| 华蓥市| 元阳县| 彩票| 交城县| 普宁市| 德江县| 本溪| 孝义市| 松潘县| 日土县| 镇康县| 田东县| 紫阳县| 金沙县| 广东省| 仙居县| 景德镇市| 田东县| 永春县| 杨浦区| 勃利县| 黑山县| 金塔县| 兰考县| 信丰县| 鄯善县| 永平县| 中宁县| 沁源县| 烟台市| 洪湖市|