`

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
分享到:
评论

相关推荐

    Programming Ruby 2nd.pdf

    它通过引入诸如块(blocks)、枚举(enumerators)和元编程(metaprogramming)等高级特性,为开发者提供了更多的灵活性和创造力空间。这些特性使得Ruby成为了一种既能高效处理复杂逻辑又能保持代码简洁的语言。 **...

    24_ruby_algorithm_

    3. **枚举器(Enumerators)**:Ruby 提供强大的枚举器类,可以帮助我们轻松地遍历所有可能的运算组合。例如,可以使用 `product` 方法来组合数字和运算符,然后用 `each_with_index` 追踪运算过程。 4. **数组操作...

    peer-enumerators.zip_micewfy_plant4l4_数据挖掘

    标题中的“peer-enumerators.zip_micewfy_plant4l4_数据挖掘”暗示了这是一个与数据挖掘相关的项目,可能涉及到算法实现和特定的工具或框架,如“micewfy”和“plant4l4”。然而,“micewfy”和“plant4l4”并不是...

    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”,我们首先要了解几个概念,包括Frobenius环、m-spotty Rosenbloom-...

    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 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 ...

    bada2.0培训_2_2_Base_Collection

    #### 2.2 Enumerators - 枚举器 枚举器是一种特殊的接口,用于简化对集合的迭代过程。它们不提供修改集合的能力,仅当集合未发生变化时保持有效。枚举器通常包括`GetCurrent()`、`MoveNext()`和`Reset()`等方法,...

    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从枚举变量获取名称,...

    XE开发英文档

    以及Delphi 2010的增强,如“Custom Attributes”,“Enhanced RTTI”,“Scoped Enumerators”,“Class Constructors & Destructors”,“Delayed Dynamic Link Libraries”等。 7. 项目配置和管理(Project ...

    c++技术术语.doc

    根据提供的文档信息,我们可以整理出一系列C++技术术语及其解释,这些术语对于理解C++编程语言的基本概念至关重要。下面是对这些术语的详细说明: ### Abstract 抽象 - **定义**:抽象是一种从具体事物中抽取共性...

    Dot.net代码编写规范

    24. **枚举器应是强类型的** (Enumerators should be strongly typed) 强类型枚举更安全,避免类型转换错误。 25. **枚举应具有 0 值** (Enums should have zero value) 0 值通常表示默认或未设置的状态,方便...

Global site tag (gtag.js) - Google Analytics