好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ruby way之Enumerables 及Iterator的区别

ruby way之Enumerables 及Iterator的区别

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

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

1 inject方法 

先看一个很简单的例子: 

Ruby代码

nums = [3,5,7,9,11,13]   sum = nums.inject(0) {|x,n| x+n }  



这个结果就是 将nums的元素一次累加起来付给x,然后最终返回x.而inject的参数0的意思是x的初始值是0. 

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

Ruby代码

sum = 0   nums. each  {|n| sum += n }   puts sum  



如果你忽略了inject的第一个参数,则它将会把所要迭代的数组的第一个元素作为那个累加值的默认值,然后从下一个元素开始迭代: 

Ruby代码

sum = nums.inject {|x,n| x+n }      #和下面的代码是等价的       sum = nums[0]   nums[1..-1]. each  {|n| sum += n }  



下面可以看一下这个比较复杂的例子: 

Ruby代码

words = %w[ alpha beta gamma delta epsilon eta theta ]   longest_word=words.inject  do  |best,w|     w.length > best.length ? w : best   end    puts longest_word  



这个例子的结果也就是words得到数组里面最长的那个元素 。 

2 使用量词符. 

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

Ruby代码

nums = [1,3,5,8,9]      # 这些元素有一个奇数吗?    flag1 = nums.any? {|x| x % 2 == 0 }     # true       #这些元素都是偶数吗?    flag2 = nums.all? {|x| x % 2 == 0 }     # false   



如果没有block参数时呢: 

Ruby代码

flag1 = list.all?    # list  包含no falses 或者nils    flag1 = list.any?    # list contains at least one true value (non-nil                         #   or non-false)   



3 partition 方法 

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

Java代码

nums = [ 1 ,  2 ,  3 ,  4 ,  5 ,  6 ,  7 ,  8 ,  9 ]      odd_even = nums.partition {|x| x %  2  ==  1  }   # [[ 1 , 3 , 5 , 7 , 9 ],[ 2 , 3 , 4 , 6 , 8 ]]      under5 = nums.partition {|x| x <  5  }   # [[ 1 , 2 , 3 , 4 ],[ 5 , 6 , 7 , 8 , 9 ]]      squares = nums.partition {|x| Math.sqrt(x).to_i** 2  == x }   # [[ 1 , 4 , 9 ],[ 2 , 3 , 5 , 6 , 7 , 8 ]]  



代码很容易懂,也就是partition将数组中的每一个元素都传进它的block,然后用返回值来进行分组: 

4 分组迭代 

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

Ruby代码

require  'enumerator'       arr = [1,2,3,4,5,6,7,8,9,10]   arr.each_slice(3)  do  |triple|     puts triple.join( "," )   end    #这里传入的3,也就意味着,每次传进去三个元素.    # Output:    # 1,2,3    # 4,5,6    # 7,8,9    # 10   



这里还有一个each_cons 方法,这个是从lisp得来的,看下面的代码: 

Ruby代码

require  'enumerator'       arr = [1,2,3,4,5,6,7,8,9,10]   arr.each_cons(3)  do  |triple|     puts triple.join( "," )   end       # Output:    # 1,2,3    # 2,3,4    # 3,4,5    # 4,5,6    # 5,6,7    # 6,7,8    # 7,8,9    # 8,9,10   




5 使用Enumerator对象 

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

看下面的代码: 

Ruby代码

require  'enumerator'       class  Foo      def  every        yield  3        yield  2        yield  1        yield  4      end    end       foo = Foo. new       # 传递一个对象和它的iterator 名字    enum = Enumerable::Enumerator. new (foo, :every )      enum. each  {|x| p x }      # Print out the items    array = enum.to_a         # [3,2,1,4]    sorted = enum.sort        # [1,2,3,4]   



其实上面的那些转换代码和下面的代码是等价的: 

Ruby代码

enum = []   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代码

array = [5,3,1,2]      discrete = array.enum_slice(2)   # Same as: Enumerable::Enumerator.new(array,:each_slice,2)       overlap  = array.enum_cons(2)   # Same as: Enumerable::Enumerator.new(array,:each_cons,2)       discrete. each  {|x| puts x.join( "," ) }   # Output:    # 5,3    # 1,2       overlap. each  {|x| puts x.join( "," ) }   # Output:    # 5,3    # 3,1    # 1,2   



6使用Generator 对象 


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

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

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

Ruby代码

require  'generator'       array = [7,8,9,10,11,12]      gen = Generator. new (array)      what  = gen.current     # 7    where = gen.index       # 0  (same as pos)       while  gen.current < 11  and  gen. next ?      puts  "#{gen.index},#{gen.next}"    end       #0,7 1,8 2,9 3,10       puts gen.current        # 11    puts gen. next            # 11    puts gen.index          # 5    (index same as pos)    puts gen. next ?          # true   (next? same as end?)    puts gen. next            # 12    puts gen. next ?          # false   




这里还有一个prev 方法,来得到上一个迭代的元素,而rewind 方法,则是重新将迭代的位置设置到开始的位置. 

这边要注意的是generator 库是使用continuations实现的,而在ruby中这个是计算昂贵(computationally expensive,)的过程,因此如果如果有很多的大数字的话,使用generator 将会非常缓慢.

查看更多关于ruby way之Enumerables 及Iterator的区别的详细内容...

  阅读:44次

上一篇: 手动安装gem

下一篇:git reset HEAD^