2010-05-19 51 views
11

为什么要使用to_enum方法而不是直接使用对象来为Ruby中的对象创建代理引用?我想不出有什么实际用途,试图理解这个概念&在哪里有人可能会使用它,但我所看到的所有例子似乎非常微不足道。在Ruby中使用to_enum创建可枚举对象的优点是什么?

例如,为什么要用:中

"hello".enum_for(:each_char).map {|c| c.succ } 

代替

​​

我知道这是一个很简单的例子,没有任何人有任何真实世界的例子?

回答

1

这不是您的问题的答案,但希望它是相关的。

在你的第二个例子你没有通过调用块each_char。当没有阻止each_char时被调用返回Enumerator,所以你的例子实际上只是两种做同样事情的方法。 (即两个结果在创建一个枚举对象的。)

irb(main):016:0> e1 = "hello".enum_for(:each_char) 
=> #<Enumerator:0xe15ab8> 
irb(main):017:0> e2 = "hello".each_char 
=> #<Enumerator:0xe0bd38> 
irb(main):018:0> e1.map { |c| c.succ } 
=> ["i", "f", "m", "m", "p"] 
irb(main):019:0> e2.map { |c| c.succ } 
=> ["i", "f", "m", "m", "p"] 
12

大多数内置在接受块将在返回的情况下一个枚举的方法没有块提供(如String#each_char在你的例子)。对于这些,没有理由使用to_enum;两者都会产生同样的效果。

但是,有些方法不返回枚举器。在这种情况下,您可能需要使用to_enum

# How many elements are equal to their position in the array? 
[4, 1, 2, 0].to_enum(:count).each_with_index{|elem, index| elem == index} #=> 2 

再举一个例子,Array#product#uniq#uniq!没有使用接受块。在1.9.2中,这被改变了,但为了保持兼容性,没有块的表单不能返回Enumerator。你仍然可以在“手动”使用to_enum得到一个枚举:

require 'backports/1.9.2/array/product' # or use Ruby 1.9.2+ 
# to avoid generating a huge intermediary array: 
e = many_moves.to_enum(:product, many_responses) 
e.any? do |move, response| 
    # some criteria 
end 

的主要用途是to_enum当你实现自己的迭代方法。您通常会将其作为第一行:

def my_each 
    return to_enum :my_each unless block_given? 
    # ... 
end 
+0

它也是有用的,如果你使用第三方库,唐”返回一个枚举器。 – 2011-12-01 07:43:41

2

我认为这与内部和外部迭代器有关。当您返回像这样的统计员时:

p = "hello".enum_for(:each_char) 

p是一个外部枚举器。外部迭代器的一个优点是:

外部迭代器比内部迭代器更灵活。例如,比较两个集合与外部迭代器的相等性是很容易的,但对于内部迭代器来说实际上是不可能的。但另一方面,内部迭代器更易于使用,因为它们为您定义了迭代逻辑。 [来自Ruby编程语言手册,ch。 5.3]

因此,与外部迭代器,你可以做,例如:

p = "hello".enum_for(:each_char) 
loop do 
    puts p.next 
end 
2

比方说,我们要采取键数组和值数组和散列缝起来:

随着#to_enum

def hashify(k, v) 
    keys = k.to_enum(:each) 
    values = v.to_enum(:each) 
    hash = [] 
    loop do 
    hash[keys.next] = values.next 
    # No need to check for bounds, 
    # as #next will raise a StopIteration which breaks from the loop 
    end 
    hash 
end 

无# to_enum:

def hashify(k, v) 
    hash = [] 
    keys.each_with_index do |key, index| 
    break if index == values.length 
    hash[key] = values[index] 
    end 
    hash 
end 

阅读第一种方法比较容易,你不觉得吗?不容易,但想象一下,如果我们以某种方式操纵3个阵列的物品? 5? 10?

+1

可怕的例子。使用'next'会杀死你的表现。这两个示例都不以相同的方式处理大小差异(一个提高,另一个停止)。 – 2014-08-06 15:15:53

0

对于大型或无限的发生器对象来说,这非常棒。例如,以下将给出针对整个斐波纳契序列的枚举器,从0到无穷大。

def fib_sequence 
    return to_enum(:fib_sequence) unless block_given? 
    yield 0 
    yield 1 
    x,y, = 0, 1 
    loop { x,y = y,x+y; yield(y) } 
end 

to_enum有效地让你写这与普通yields而不必惹Fiber秒。

然后可以分析它,只要你想,这将是非常高效存储,因为没有阵列将被存储在内存中:

module Slice 
    def slice(range) 
     return to_enum(:slice, range) unless block_given? 
     start, finish = range.first, range.max + 1 
     copy = self.dup 
     start.times { copy.next } 
     (finish-start).times { yield copy.next } 
    end 
end 
class Enumerator 
    include Slice 
end 

fib_sequence.slice(0..10).to_a 
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 
fib_sequence.slice(10..20).to_a                               
#=> [55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]