免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4962 | 回复: 4
打印 上一主题 下一主题

ruby way之Enumerables 及Iterator的区别 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-01-21 14:47 |只看该作者 |倒序浏览
ruby way之Enumerables 及Iterator的区别




Enumerable 模块是一个很重要的模块,ruby中的很多类都有mix这个模块,比如array,hash等等。因此如果你想要自己定义一个collection,则不要继承一个存在的collection,而是应该mix Enumerable 模块。

数组是最常用,最有代表性的mix Enumerable模块的一个集合。因此下面的都会默认用数组来举例子

1 inject方法

先看一个很简单的例子:

Ruby代码
  1. nums = [3,5,7,9,11,13]  
  2. sum = nums.inject(0) {|x,n| x+n }
复制代码
这个结果就是 将nums的元素一次累加起来付给x,然后最终返回x.而inject的参数0的意思是x的初始值是0.

于是上面的代码其实也就等同于下面的代码:

Ruby代码
  1. sum = 0  
  2. nums.each {|n| sum += n }  
  3. puts sum  
复制代码
如果你忽略了inject的第一个参数,则它将会把所要迭代的数组的第一个元素作为那个累加值的默认值,然后从下一个元素开始迭代:

Ruby代码
  1. sum = nums.inject {|x,n| x+n }  
  2.   
  3. #和下面的代码是等价的  
  4.   
  5. sum = nums[0]  
  6. nums[1..-1].each {|n| sum += n }  
复制代码
下面可以看一下这个比较复杂的例子:

Ruby代码
  1. words = %w[ alpha beta gamma delta epsilon eta theta ]  
  2. longest_word=words.inject do |best,w|  
  3.   w.length > best.length ? w : best  
  4. end  
  5. puts longest_word  
复制代码
这个例子的结果也就是words得到数组里面最长的那个元素 。

2 使用量词符.

使用any?和all?方法能够很容易测试一个集合:

Ruby代码
  1. nums = [1,3,5,8,9]  
  2.   
  3. # 这些元素有一个奇数吗?  
  4. flag1 = nums.any? {|x| x % 2 == 0 }    # true  
  5.   
  6. #这些元素都是偶数吗?  
  7. flag2 = nums.all? {|x| x % 2 == 0 }    # false  
复制代码
如果没有block参数时呢:

Ruby代码
  1. flag1 = list.all?   # list  包含no falses 或者nils  
  2. flag1 = list.any?   # list contains at least one true value (non-nil  
  3.                     #   or non-false)  
复制代码
3 partition 方法

简而言之,partition 方法就是用来分组的,也就是说,它可以将一个数组根据一定的规则分为不同的组.

Java代码
  1. nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]  
  2.   
  3. odd_even = nums.partition {|x| x % 2 == 1 }  
  4. # [[1,3,5,7,9],[2,3,4,6,8]]  
  5.   
  6. under5 = nums.partition {|x| x < 5 }  
  7. # [[1,2,3,4],[5,6,7,8,9]]  
  8.   
  9. squares = nums.partition {|x| Math.sqrt(x).to_i**2 == x }  
  10. # [[1,4,9],[2,3,5,6,7,8]]  
复制代码
代码很容易懂,也就是partition将数组中的每一个元素都传进它的block,然后用返回值来进行分组:

4 分组迭代

以前我们所介绍的迭代都是每次迭代一个元素,如果我们想要迭代一个组的时候,我们可以使用each_slice:

Ruby代码
  1. require 'enumerator'  
  2.   
  3. arr = [1,2,3,4,5,6,7,8,9,10]  
  4. arr.each_slice(3) do |triple|  
  5.   puts triple.join(",")  
  6. end  
  7. #这里传入的3,也就意味着,每次传进去三个元素.  
  8. # Output:  
  9. # 1,2,3  
  10. # 4,5,6  
  11. # 7,8,9  
  12. # 10  
复制代码
这里还有一个each_cons 方法,这个是从lisp得来的,看下面的代码:

Ruby代码
  1. require 'enumerator'  
  2.   
  3. arr = [1,2,3,4,5,6,7,8,9,10]  
  4. arr.each_cons(3) do |triple|  
  5.   puts triple.join(",")  
  6. end  
  7.   
  8. # Output:  
  9. # 1,2,3  
  10. # 2,3,4  
  11. # 3,4,5  
  12. # 4,5,6  
  13. # 5,6,7  
  14. # 6,7,8  
  15. # 7,8,9  
  16. # 8,9,10  
复制代码
5 使用Enumerator对象

Enumerator 一般是作为一个包装器,将一个iterator 方法转换成一个完全的Enumerable.当完成转换之后,这个对象将会有很多方法可以使用.

看下面的代码:

Ruby代码
  1. require 'enumerator'  
  2.   
  3. class Foo  
  4.   def every  
  5.     yield 3  
  6.     yield 2  
  7.     yield 1  
  8.     yield 4  
  9.   end  
  10. end  
  11.   
  12. foo = Foo.new  
  13.   
  14. # 传递一个对象和它的iterator 名字  
  15. enum = Enumerable::Enumerator.new(foo,:every)  
  16.   
  17. enum.each {|x| p x }     # Print out the items  
  18. array = enum.to_a        # [3,2,1,4]  
  19. sorted = enum.sort       # [1,2,3,4]  
复制代码
其实上面的那些转换代码和下面的代码是等价的:

Ruby代码
  1. enum = []  
  2. foo.every {|x| enum << x }  
复制代码
我们下面还有另外一方法,来转换一个对象到Enumerable.

如果enumerator 被require,Object 将会有一个enum_for 方法,因此我们的转换方式将变为:

Ruby代码
enum = foo.enum_for(:every)  


对应于each_slice 和each_cons,当require了enumerator之后对象还会有enum_slice和enum_cons方法,这边要注意Enumerator.new方法,还能多加参数,它的后面的参数也就是所传进去的方法的参数:

Ruby代码
  1. array = [5,3,1,2]  
  2.   
  3. discrete = array.enum_slice(2)  
  4. # Same as: Enumerable::Enumerator.new(array,:each_slice,2)  
  5.   
  6. overlap  = array.enum_cons(2)  
  7. # Same as: Enumerable::Enumerator.new(array,:each_cons,2)  
  8.   
  9. discrete.each {|x| puts x.join(",") }  
  10. # Output:  
  11. # 5,3  
  12. # 1,2  
  13.   
  14. overlap.each {|x| puts x.join(",") }  
  15. # Output:  
  16. # 5,3  
  17. # 3,1  
  18. # 1,2  
复制代码
6使用Generator 对象


一般ruby的迭代器都是内置的,迭代器通过它的block来操作逻辑.

ruby还有一个外部的迭代器,这种迭代器它能够提供给你数据,让你来操作,比如IO中的getline函数。

generator 库能够转换内置的迭代器到外部的迭代器,它提供一个类似于IO的接口,这个接口含有next, rewind, 和 end?这样的方法:

Ruby代码
  1. require 'generator'  
  2.   
  3. array = [7,8,9,10,11,12]  
  4.   
  5. gen = Generator.new(array)  
  6.   
  7. what  = gen.current    # 7  
  8. where = gen.index      # 0  (same as pos)  
  9.   
  10. while gen.current < 11 and gen.next?   
  11.   puts "#{gen.index},#{gen.next}"  
  12. end  
  13.   
  14. #0,7 1,8 2,9 3,10  
  15.   
  16. puts gen.current       # 11  
  17. puts gen.next          # 11  
  18. puts gen.index         # 5    (index same as pos)  
  19. puts gen.next?         # true   (next? same as end?)  
  20. puts gen.next          # 12  
  21. puts gen.next?         # false  
复制代码

论坛徽章:
0
2 [报告]
发表于 2011-01-21 15:40 |只看该作者
不错啊。。是转贴还是原创?

论坛徽章:
0
3 [报告]
发表于 2011-01-24 14:31 |只看该作者
不错不错啊。

论坛徽章:
0
4 [报告]
发表于 2012-02-07 16:08 |只看该作者
不错啊。这些不错。

论坛徽章:
7
戌狗
日期:2013-12-15 20:43:38技术图书徽章
日期:2014-03-05 01:33:12技术图书徽章
日期:2014-03-15 20:31:17未羊
日期:2014-03-25 23:48:20丑牛
日期:2014-04-07 22:37:44巳蛇
日期:2014-04-11 21:58:0915-16赛季CBA联赛之青岛
日期:2016-03-17 20:36:13
5 [报告]
发表于 2012-02-08 18:27 |只看该作者
很有代表性啊。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP