开发者社区> boxti> 正文

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 

  

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
别梦依稀咒逝川,Ruby二十八年前|M1芯片Mac os系统配置Ruby(3.0.0) on Rails(6.1.1)开发环境
在每个开发者心里,都会有一门“最好”的语言,在这个世界的某个深处,在一些矫矫不群的人们心中,这门语言的名字叫做Ruby,它今年二十八岁了,历史和Java一样的悠久,但是它没有大厂背书、它的性能被开发者诟病、时至今日依然无法高效利用多核资源,甚至于它每年都要被“死亡”一次,相比于有太阳计算机系统、甲骨文、IBM 这些大公司支持的 Java,它是那么的一无所有,但是,它又拥有全世界最虔诚的“信徒”,拥有最活跃的开发者社区,这一切,又让它是那么的应有尽有。是的,这就是Rubyist的理念:有的时候,你想证明给一万个人看,到后来,你发现只得到了一个明白的人,那就够了。
138 0
基于Docker在Win10平台搭建Ruby on Rails 6.0框架开发环境
2023年,“非著名Web框架”--Ruby on Rails已经18岁了。在今年,Rails 6.0趋于完善,除了拿掉讨厌的Jquery,Webpacker 也成为默认前端打包方案,Sprockets 开始软着陆,未来很可能会和Jquery一样被彻底废弃,这就是历史的进程。
78 0
CVE-2018-3760 Ruby On Rails 路径穿越漏洞
CVE-2018-3760 Ruby On Rails 路径穿越漏洞
75 0
CVE-2019-5418 Ruby on Rails 路径穿越与任意文件读取漏洞
CVE-2019-5418 Ruby on Rails 路径穿越与任意文件读取漏洞
71 0
【Ruby on Rails全栈课程】5.5 项目上线--nginx+unicorn部署项目、域名映射
1、域名映射准备材料 云服务器以及域名(参考5.1 购买服务器、域名) nginx unicorn 2、安装以及设置nginx
193 0
+关注
boxti
12535
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载