Ruby Fiber指南(四)迭代器

    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

    文章转自庄周梦蝶  ,原文发布时间 2010-03-12 

  

时间: 2024-10-26 21:27:00

Ruby Fiber指南(四)迭代器的相关文章

Ruby Fiber指南(一)基础

Ruby Fiber指南(一)基础     Ruby Fiber指南(二)参数传递     Ruby Fiber指南(三)过滤器    Ruby Fiber指南(四)迭代器     Ruby Actor指南(五)实现Actor         这是一个Ruby Fiber的教程,基本是按照<Programming in lua>中讲述协程章节的顺序来介绍Ruby Fiber的,初步分为5节:基础.参数传递.过滤器.迭代器.应用.这是第一节,介绍下Ruby Fiber的基础知识.     Rub

Ruby Fiber指南(二)参数传递

  Ruby Fiber指南(一)基础     Ruby Fiber指南(二)参数传递     Ruby Fiber指南(三)过滤器    Ruby Fiber指南(四)迭代器     Ruby Actor指南(五)实现Actor     这一篇其实也算是Fiber编程的基础篇,只不过参数传递算是一个比较重要的主题,因此独立一节.参数传递发生在两个Fiber之间,作为Fiber之间通讯的一个主要手段.     首先,我们可以通过resume调用给Fiber的block传递参数: 1 #resum

Ruby Fiber指南(三)过滤器

Ruby Fiber指南(一)基础     Ruby Fiber指南(二)参数传递     Ruby Fiber指南(三)过滤器    Ruby Fiber指南(四)迭代器     Ruby Actor指南(五)实现Actor      在学习了Fiber的基础知识之后,可以尝试用Fiber去做一些比较有趣的事情.这一节将讲述如何使用Fiber来实现类似unix系统中的管道功能.在unix系统中,可以通过管道将多个命令组合起来做一些强大的功能,最常用的例如查找所有的java进程: ps aux|

Lua、LuaJIT Coroutine和Ruby Fiber的切换效率对比

最近重读了<Programming Lua>,对协程做了重点复习.众所周知,Ruby1.9引入了Fiber,同样是coroutine,不过Ruby Fiber支持全对称协程(通过fiber库),而Lua只支持所谓半对称协程.     这里将对Lua.LuaJIT和Ruby Fiber的切换效率做个对比测试,测试场景很简单:两个coroutine相互切换达到5000万次,统计每秒切换的次数,各测试多次取最佳.     lua的程序如下:     c1=coroutine.create(funct

Ruby中Block和迭代器的使用讲解_ruby专题

我们来简单地描述Ruby的一个独特特性.Block,一种可以和方法调用相关联的代码块,几乎就像参数一样.这是一个不可思议的功能强大的特性. 可以用Block实现回调(但它比Java的匿名内部(anonymous inner)类更简单),传递一组代码(但它远比c的函数指针灵活),以及实现迭代器. Block只是在花括号或者do...end之间的一组代码. {puts "Hello"} #this is a block do ### club.enroll(person) #and so

MEF程序设计指南四:使用MEF声明导出(Exports)与导入(Imports)

在MEF中,使用[System.ComponentModel.Composition.ExportAttribute]支持多种级别的导出部件配置,包括类.字段.属性以及方法 级别的导出部件,通过查看ExportAttribute的源代码就知道ExportAttribute被定义为 Attribute,并为其设置了使用范围. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA

Programming Ruby读书笔记(四)

Ruby正则表达式 三种表示方法: Regexp.new('^s*[a-z]') /^s*[a-z]/ %r...{^s*[a-z]} 测试代码: def show_reqexp(a, re) if a =~ re "#{$`} << #{$&} >> #{$'}" else "no match" end end puts show_reqexp("Fats Waller", /ll/) 结果:Fats Wa &l

设计模式 ( 十四 ) 迭代器模式Iterator(对象行为型)

1.概述 类中的面向对象编程封装应用逻辑.类,就是实例化的对象,每个单独的对象都有一个特定的身份和状态.单独的对象是一种组织代码的有用方法,但通常你会处理一组对象或者集合. 集合不一定是均一的.图形用户界面框架中的 Window 对象可以收集任意数量的控制对象 - Menu.Slider 和 Button.并且,集合的实现可以有多种方式:PHP 数字是一个集合,但也是一个散列表,一个链接列表,一个堆栈以及队列. 例子1:电视遥控器的频道遍历 2.问题 如何操纵任意的对象集合?  如一个列表(Li

shell脚本学习指南[四](Arnold Robbins &amp; Nelson H.F. Beebe著)_linux shell

回忆起一件事情:之前用linux寻找中文输入法的时候,在百度输入了fcitx,然后结果上边有个,您要找的是不是: 讽刺腾讯 .本来一直记不住这个输入法名字,不过以后哥就记住这个输入法的名字是怎么拼了,感谢百度. 第九章awk的惊人表现 awk的调用可以定义变量.提供程序并且指定输入文件,语法: 复制代码 代码如下: awk [ -F fs ] [ -v var=value ... ] 'program' [ -- ] [ var=value ... ] [file(s) ]awk [ -F fs