Ruby Fiber指南(四)迭代器
2023-03-14 10:31:10 时间
Ruby Fiber指南(一)基础
Ruby Fiber指南(二)参数传递
Ruby Fiber指南(三)过滤器
Ruby Fiber指南(四)迭代器
Ruby Actor指南(五)实现Actor
上一节介绍了利用Fiber实现类unix管道风格的过滤链,这一节将介绍利用Fiber来实现迭代器,我们可以将循环的迭代器看作生产者-消费者模式的特殊的例子。迭代函数产生值给循环体消费。所以可以使用Fiber来实现迭代器。协程的一个关键特征是它可以不断颠倒调用者与被调用者之间的关系,这样我们毫无顾虑的使用它实现一个迭代器,而不用保存迭代函数返回的状态,也就是说无需在迭代函数中保存状态,状态的保存和恢复交由Fiber自动管理。
这一节的介绍以一个例子贯穿前后,我们将不断演化这个例子,直到得到一个比较优雅的可重用的代码,这个例子就是求数组的全排列。如数组[1,2,3]的全排列包括6种排列:
第一步,将打印任务修改为Fiber#yield,生产者产生一个排列后将结果传递给消费者并让出执行权:
第二步,实现一个迭代器工厂,返回一个匿名的迭代函数,迭代函数请求Fiber产生一个新的排列:
这样一来我们就可以利用一个while循环来打印全排列:
注意到,在perm方法中有一个很常见的模式,就是将对Fiber的resume封装在一个匿名函数内,在lua为了支持这种模式还特意提供了一个coroutine.wrap方法来方便编程,在Ruby Fiber中却没有,不过我们可以自己实现下,利用open-class的特性实现起来非常简单:
Fiber#wrap方法跟new方法一样,创建一个新的Fiber,但是返回的是一个匿名函数,这个匿名函数负责去调用fiber的resume,利用wrap改写perm方法变得更简洁:
但是还不够,while循环的方式还是不够优雅,每次都需要明确地调用迭代器的call方法,这一点让人挺不舒坦,如果能像for...in那样的泛型循环就好了,我们知道Ruby中的for...in其实是一个语法糖衣,都是转变成调用集合的each方法并传入处理的block,因此,要想实现一个优雅的迭代器,我们做下封装就好了:
那么现在的perm方法变成了创建一个迭代器FiberIterator:
文章转自庄周梦蝶 ,原文发布时间 2010-03-12
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实现如下:3 2 1
3 1 2
1 3 2
2 1 3
1 2 3
#全排列的递归实现
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改写如下: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#yield,生产者产生一个排列后将结果传递给消费者并让出执行权:
def permgen (a, n)
if n == 0 then
Fiber.yield(a)
……
end
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
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
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
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
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
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来调用迭代器了FiberIterator.new{ permgen(a,a.size) }
end
it=perm([1,2,3,4])
for a in it
printResult(a)
end
for a in it
printResult(a)
end
文章转自庄周梦蝶 ,原文发布时间 2010-03-12
相关文章
- 在 Go 里用 CGO?这 7 个问题你要关注!
- 9款优秀的去中心化通讯软件 Matrix 的客户端
- 求职数据分析,项目经验该怎么写
- 在OKR中,我看到了数据驱动业务的未来
- 火山引擎云原生大数据在金融行业的实践
- OpenHarmony富设备移植指南(二)—从postmarketOS获取移植资源
- 《数据成熟度指数》报告:64%的企业领袖认为大多数员工“不懂数据”
- OpenHarmony 小型系统兼容性测试指南
- 肯睿中国(Cloudera):2023年企业数字战略三大趋势预测
- 适用于 Linux 的十大命令行游戏
- GNOME 截图工具的新旧截图方式
- System76 即将推出的 COSMIC 桌面正在酝酿大变化
- 2GB 内存 8GB 存储即可流畅运行,Windows 11 极致精简版系统 Tiny11 发布
- 迎接 ecode:一个即将推出的具有全新图形用户界面框架的现代、轻量级代码编辑器
- loongarch架构介绍(三)—地址翻译
- Go 语言怎么解决编译器错误“err is shadowed during return”?
- 敏捷:可能被开发人员遗忘的部分
- Denodo预测2023年数据管理和分析的未来
- 利用数据推动可持续发展
- 在 Vue3 中实现 React 原生 Hooks(useState、useEffect),深入理解 React Hooks 的