莊周夢蝶

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

          Ruby Fiber指南(四)迭代器

          Posted on 2010-03-12 12:48 dennis 閱讀(3285) 評論(0)  編輯  收藏 所屬分類: 動態語言my open-source

              Ruby Fiber指南(一)基礎
              Ruby Fiber指南(二)參數傳遞
              Ruby Fiber指南(三)過濾器
              Ruby Fiber指南(四)迭代器
              Ruby Actor指南(五)實現Actor

               上一節介紹了利用Fiber實現類unix管道風格的過濾鏈,這一節將介紹利用Fiber來實現迭代器,我們可以將循環的迭代器看作生產者-消費者模式的特殊的例子。迭代函數產生值給循環體消費。所以可以使用Fiber來實現迭代器。協程的一個關鍵特征是它可以不斷顛倒調用者與被調用者之間的關系,這樣我們毫無顧慮的使用它實現一個迭代器,而不用保存迭代函數返回的狀態,也就是說無需在迭代函數中保存狀態,狀態的保存和恢復交由Fiber自動管理。

               這一節的介紹以一個例子貫穿前后,我們將不斷演化這個例子,直到得到一個比較優雅的可重用的代碼,這個例子就是求數組的全排列。如數組[1,2,3]的全排列包括6種排列:
          2 3 1
          3 2 1
          3 1 2
          1 3 2
          2 1 3
          1 2 3
          全排列的遞歸算法實現很簡單,我們用Ruby實現如下:

          #全排列的遞歸實現
          def permgen (a, n)
              
          if n == 0 then
                 printResult(a)
              
          else
                  n.times do 
          |i|
                     a[n
          -1], a[i] = a[i], a[n-1]
                     permgen(a, n 
          - 1)
                     a[n
          -1], a[i] = a[i], a[n-1]
                 end
              end
          end

          def printResult (a)
              puts a.join(
          " ")
          end

          permgen([
          1,2,3,4],4)
          算法的思路是這樣:
          將數組中的每一個元素放到最后,依次遞歸生成所有剩余元素的排列,沒完成一個排列就打印出來。很顯然,這里有消費者和生產者的關系存在,生產者負責產生排列,消費者負責打印任務,整個程序由消費者驅動,因此用Fiber改寫如下:

          第一步,將打印任務修改為Fiber#yield,生產者產生一個排列后將結果傳遞給消費者并讓出執行權:
          def permgen (a, n)
              
          if n == 0 then
                 Fiber.
          yield(a)
              ……
          end

          第二步,實現一個迭代器工廠,返回一個匿名的迭代函數,迭代函數請求Fiber產生一個新的排列:
          def perm(a)
            f
          =Fiber.new do
                  permgen(a,a.size)
              end
            
          return lambda{ f.resume if f.alive? }
          end

          這樣一來我們就可以利用一個while循環來打印全排列:
          it=perm([1,2,3,4])

          while a=it.call
             printResult(a)
          end

              注意到,在perm方法中有一個很常見的模式,就是將對Fiber的resume封裝在一個匿名函數內,在lua為了支持這種模式還特意提供了一個coroutine.wrap方法來方便編程,在Ruby Fiber中卻沒有,不過我們可以自己實現下,利用open-class的特性實現起來非常簡單:
          #為Fiber添加wrap方法
          class Fiber
            
          def self.wrap
              
          if block_given?
                f
          =Fiber.new do |*args|
                   
          yield *args
                end
                
          return lambda{|*args| f.resume(*args) if f.alive? }
              end
            end
          end
           
              Fiber#wrap方法跟new方法一樣,創建一個新的Fiber,但是返回的是一個匿名函數,這個匿名函數負責去調用fiber的resume,利用wrap改寫perm方法變得更簡潔:
          def perm(a)
            Fiber.wrap{ permgen(a,a.size) }
          end

              但是還不夠,while循環的方式還是不夠優雅,每次都需要明確地調用迭代器的call方法,這一點讓人挺不舒坦,如果能像for...in那樣的泛型循環就好了,我們知道Ruby中的for...in其實是一個語法糖衣,都是轉變成調用集合的each方法并傳入處理的block,因此,要想實現一個優雅的迭代器,我們做下封裝就好了:

          class FiberIterator
            
          def initialize
              @fiber_wrap
          =Fiber.wrap do
                  
          yield
              end
            end
            
          def each
              
          while value=@fiber_wrap.call
                
          yield value
              end
            end
          end

          那么現在的perm方法變成了創建一個迭代器FiberIterator:
          def perm(a)
            FiberIterator.new{ permgen(a,a.size) }
          end
          這樣一來我們就可以通過for...in來調用迭代器了

          it=perm([1,2,3,4])
          for a in it
            printResult(a)
          end


             

              
          主站蜘蛛池模板: 虞城县| 广灵县| 阿克陶县| 将乐县| 金昌市| 白河县| 兴城市| 清水河县| 康保县| 中西区| 聂荣县| 南部县| 南和县| 长泰县| 峡江县| 修文县| 贺兰县| 浙江省| 柳林县| 观塘区| 龙山县| 梧州市| 福建省| 吉安市| 常熟市| 五原县| 昭通市| 乐都县| 文山县| 张家港市| 隆德县| 沙洋县| 丽江市| 和林格尔县| 南汇区| 福贡县| 江阴市| 兖州市| 大足县| 荔波县| 中宁县|