`

Ruby Enumerators

    博客分类:
  • Ruby
阅读更多
一个enumerator是一个用来枚举其他对象的Enumerable对象。在Ruby1.8,需要require 'enumerator',在Ruby1.9已经内建,不需要再require,并且进行了增强。
Enumerators是类Enumerable::Enumerator,所以你可以直接new出来实例,但是通常使用to_enum或者使用enum_for(Object的方法)。如果没有参数,to_enum返回的enumerator,他的each直接delegate到目标对象的each。第一个参数是目标对象的迭代方法symbol,其他的参数都被传递给这个命名的方法。在Ruby 1.9,String已经不是Enumerable了,但是它定义了三个迭代方法each_char,each_byte,each_line。如果我们想要使用其他的Enumerable方法,比如
map,我们想基于each_char迭代器。我们可以创建一个enumerator:
s = "hello"
s.enum_for(:each_char).map{|c| c.succ } # => ["i", "f", "m", "m", "p"]

事实上,上面的例子在Ruby 1.9可以不用显式的使用to_enum或者enum_for,因为
times, upto, downto,step,each以及Enumerable相关的方法自动返回enumerator.
引用

irb(main):002:0> "hello".each_char.map{|c| c.succ }
=> ["i", "f", "m", "m", "p"]

enumerator = 3.times             # An enumerator object
enumerator.each {|x| print x }   # Prints "012"
10.downto(1).select {|x| x%2==0}  # => [10,8,6,4,2]

我们举个简单的twice例子来说明这些方法的实现:
def twice
    if block_given?
      yield
      yield
    else
       self.to_enum(:twice)
    end
end

在1.9版本,enumerable对象定义了with_index方法:
s.each_char.with_index{|c,index| puts "#{index}: #{c}" }

迭代和并发修改:
和其他语言的迭代器一样,在迭代的过程修改迭代的对象,会产生不合预期的行为:
a = [1,2,3,4,5]
a.each {|x| puts "#{x},#{a.shift}" }  # prints "1,1\n3,2\n5,3"

如果在多个线程共享一个容器,一个线程修改,另一个线程迭代,也会产生类似的
不符合预期的行为,要想避免,需要在迭代之前copy:
module Enumerable
  def each_in_snapshot &block
    snapshot = self.dup    # Make a private copy of the Enumerable object
    snapshot.each &block   # And iterate on the copy
  end
end

Enumerators有哪些用法?
1.作为proxy
就像前面说的Enumerable方法像map、select默认都会调用目标对象的each方法,
但是你想使用目标对象的each_byte,each_with_index方法来迭代,一个enumerator
就是一个简单的代理,它定义了each,并且将each方法delegate到enum_for
参数指定的目标对象的方法。
src = "hello"
puts src.enum_for(:each_byte).map { |b| "%02x" % b }.join(" ")

比如你想将一个数组传递到一个方法,因为数组是可变的,你不相信这个方法会不会改变
这个数组,因为你不期望这个方法会有副作用,那么你也可以使用enum_for得到enumerator,变成不变的代理对象:
# Call this method with an Enumerator instead of a mutable array.
# This is a useful defensive strategy to avoid bugs.
process(data.to_enum)  # Instead of just process(data)

2.作为外部迭代器:
内部迭代器:迭代器自己控制迭代,将操作施用于每一个元素,比如each,select
外部迭代器:客户程序控制迭代,使用next来得到下一个元素。
外部迭代器比较灵活一些,比如可以很容易比较两个集合是否相等,但是内部迭代器
做不到。
Enumerator有each方法作为内部迭代器,来push数据到关联的代码,同时也提供了
next,作为客户端可以pull数据的外部迭代器。
使用next来获得下一个元素,但是如果没有元素了,next就会抛出StopIteration异常。
但Ruby1.9的Kernal.loop已经隐式的处理了这个异常,所以可以:
iterator = 9.downto(1)
loop do                 # Loop until StopIteration is raised
  print iterator.next   # Print next item
end
puts "bulabulabula..."

使用外部迭代来实现内部迭代:
module Iterable
  include Enumerable
  
  def each
    loop {yeild self.next}
  end
end

或者直接定义一个内部迭代方法,将有内部迭代的对象传递给他:
def iterate(iterator)
  loop { yield iterator.next }
end
iterate(9.downto(1)) {|x| print x }



3.可以代表一段代码,进行Lazy evaluation.
fib = Enumerator.new { |y|  
     a = b = 1  
     loop {  
       y << a  
       a, b = b, a + b  
     }  
}
fib.take(10) #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]  


PS:
使用外部迭代器的一个重要的特点可以实现并行迭代:假如你有两个集合,需要
成对迭代。下面来自The Ruby programming language的并行迭代的例子:
Code View:

# Call the each method of each collection in turn.
# This is not a parallel iteration and does not require enumerators.
def sequence(*enumerables, &block)
  enumerables.each do |enumerable|
    enumerable.each(&block)
  end
end

# Iterate the specified collections, interleaving their elements.
# This can't be done efficiently without external iterators.
# Note the use of the uncommon else clause in begin/rescue.
def interleave(*enumerables)
  # Convert enumerable collections to an array of enumerators.
  enumerators = enumerables.map {|e| e.to_enum }
  # Loop until we don't have any more enumerators.
  until enumerators.empty?
    begin
      e = enumerators.shift   # Take the first enumerator
      yield e.next            # Get its next and pass to the block
    rescue StopIteration      # If no more elements, do nothing
    else                      # If no exception occurred
      enumerators << e        # Put the enumerator back
    end
  end
end

# Iterate the specified collections, yielding tuples of values,
# one value from each of the collections. See also Enumerable.zip.
def bundle(*enumerables)
  enumerators = enumerables.map {|e| e.to_enum }
  loop { yield enumerators.map {|e| e.next} }
end

# Examples of how these iterator methods work
a,b,c = [1,2,3], 4..6, 'a'..'e'
sequence(a,b,c) {|x| print x}   # prints "123456abcde"
interleave(a,b,c) {|x| print x} # prints "14a25b36cde"
bundle(a,b,c) {|x| print x}     # '[1, 4, "a"][2, 5, "b"][3, 6, "c"]'


参考:《The Ruby programming language》
1
1
分享到:
评论

相关推荐

    peer-enumerators.zip_micewfy_plant4l4_数据挖掘

    高斯列主元素消去法求解矩阵方程AX B,其中A是N N的矩阵,B是N M矩阵 输入 n----方阵A的行数 a----矩阵

    enumerators:Dart数据类型的随机访问枚举

    随机访问枚举 通过提供Dart数据类型的随机访问枚举,可以对Dart函数进行随机或穷举测试的库。... import 'package:enumerators/combinators.dart' as 'c' ; main () { // c.strings is an enumeration:

    An identity between the m-spotty Rosenbloom-Tsfasman weight enumerators over finite commutative Frobenius rings

    An identity between the m-spotty Rosenbloom-Tsfasman weight enumerators over finite commutative Frobenius rings

    ARWordReport 1.6 for Delphi 3, 4, 5, 6, 7, C++Builder 5, 6.

    datasets and OnTag event.Data tables in report.Conditional blocks in report template.Enumerators in report template. Custom SQL Queries in report template.Simple report templates creation direct in ...

    Cocoa design pattern

    Mac and iPhone developers are often overwhelmed by the breadth and sophistication of the Cocoa frameworks....Fundamental patterns, such as enumerators, accessors, and two-stage creation

    Cocoa Design Patterns

    Fundamental patterns, such as enumerators, accessors, and two-stage creation Patterns that empower, such as singleton, delegates, and the responder chain Patterns that hide complexity, including ...

    enum:Golang枚举器

    :bookmark_tabs: Golang Enumerators :bookmark_tabs: 什么 Enum是一个软件包,为Go提供简单的枚举器,并具有IDE自动补全功能和任何类型的支持。它可能不是最漂亮的语法,但它是如此有用。 安装 go get github....

    life-vest:一个用于在使用Akka Streams处理数据时统一处理流和非流转换的库

    救生衣 一个库,用于在使用Akka Streams处理数据时统一处理流和非流转换。 这个名字是救生衣上的双关语,是在溪流或河流中工作的人的“制服”。... Streamable// four enumerators need to be wrapped in `S

    propcheck:Dart属性的穷举和随机测试

    import 'package:enumerators/combinators.dart' as c; import 'package:unittest/unittest.dart' ; // defines append and reverse part 'demolib.dart' ; /* --- the properties to test --- */ // this should ...

    RxPlay:让 Rx 在 Play 中运行良好! 框架。 (Scala)

    感谢的工作,我们在 Enumerators 和 Observables 之间有了一个隐式映射,我们可以将其存储在 RxPlay.scala 库中。 然后,我们看到 Iteratees 和 Enumerator 与 Play 框架深度集成。 我们的最终目标是让它们从编码器...

    Apress.Illustrated.C#.2012

    Enumerators and Iterators Introduction to LINQ Introduction to Asynchronous Programming Namespaces and Assemblies Exceptions Preprocessor Directives Reflection and Attributes Other Topics

    static_enum:C ++ static_enum

    static_enum::get_enumerators创建具有所有枚举值(按值排序)的std::array static_enum::enum_cast可以像static_cast一样用于将枚举转换为字符串或从枚举创建字符串 static_enum::to_string从枚举变量获取名称,...

    Microsoft® SQL Server™ 2008 Integration Services unleashed

    15 Looping and Foreach Enumerators ...................313 Part IV Management Services 16 SQL Server Management Studio ...................337 17 Using Source Control..........................351 18 ...

    C#5.0 2012年出版的英文书

    冝 Chapter 18: Enumerators and Iterators ..............................................................461 冝 Chapter 19: Introduction to LINQ ............................................................

    Debugging with GDB --2007年

    Table of Contents Summary of GDB . . . . . . . . ....Free software ....Contributors to GDB ....A Sample GDB Session ....Loading the Executable ....Setting Display width....Setting Breakpoints ....Running the executable ...

Global site tag (gtag.js) - Google Analytics