在Ruby中使用to_enum创建可枚举对象的优点是什么?

在Ruby中使用to_enum创建可枚举对象的优点是什么?,ruby,enumeration,Ruby,Enumeration,为什么要在Ruby中通过使用to_enum方法而不是直接使用对象来创建对对象的代理引用?我想不出这有什么实际用途,试图理解这个概念&有人可能会在哪里使用它,但我看到的所有例子似乎都很琐碎 例如,为什么使用: "hello".enum_for(:each_char).map {|c| c.succ } 而不是 "hello".each_char.map {|c| c.succ } 我知道这是一个非常简单的例子,有人有真实世界的例子吗?这不是你问题的答案,但希望它是相关的 在第二个示例中,您在不

为什么要在Ruby中通过使用to_enum方法而不是直接使用对象来创建对对象的代理引用?我想不出这有什么实际用途,试图理解这个概念&有人可能会在哪里使用它,但我看到的所有例子似乎都很琐碎

例如,为什么使用:

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

"hello".each_char.map {|c| c.succ }

我知道这是一个非常简单的例子,有人有真实世界的例子吗?

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

在第二个示例中,您在不传递块的情况下调用每个字符。当在没有块的情况下调用时,每个字符都会返回一个枚举数,因此您的示例实际上只是做同一件事的两种方法。(即,两者都会导致创建可枚举对象。)

irb(main):016:0>e1=“hello”。枚举(:每个字符)
=> #
irb(main):017:0>e2=“你好”。每个字符
=> #
irb(main):018:0>e1.map{| c | c.such}
=>[“i”、“f”、“m”、“m”、“p”]
irb(main):019:0>e2.map{| c | c.such}
=>[“i”、“f”、“m”、“m”、“p”]

大多数接受块的内置方法都会在没有提供块的情况下返回枚举数(如示例中的
String\each_char
)。对于这些,没有理由使用
来枚举
;两者的效果相同

不过,有几个方法不返回枚举数。在这种情况下,您可能需要使用
来_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中,对其进行了更改,但为了保持兼容性,没有块的表单不能返回
枚举数。仍然可以“手动”使用
进行枚举
获取枚举器:

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

我认为这与内部和外部迭代器有关。返回如下所示的枚举数时:

p = "hello".enum_for(:each_char)
p是一个外部枚举数。外部迭代器的一个优点是:

外部迭代器比内部迭代器更灵活。例如,使用外部迭代器比较两个集合是否相等很容易,但使用内部迭代器实际上是不可能的…。但另一方面,内部迭代器更容易使用,因为它们为您定义了迭代逻辑。[摘自Ruby编程语言书,第5.3章]

因此,使用外部迭代器可以执行以下操作,例如:

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

假设我们要获取一个键数组和一个值数组,并将它们缝合在一个散列中:

带#到#枚举

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
没有#到#枚举:

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?

这对于大型或无限大的生成器对象非常有用。 例如,下面将为您提供整个斐波那契序列的枚举数,从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
有效地允许您使用常规的
产量
编写此代码,而无需使用
光纤
s

然后,您可以根据需要对其进行切片,这将非常节省内存,因为内存中不会存储任何数组:

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]

如果您使用的是不返回枚举数的第三方库,那么它也很有用。使用
next
会降低您的性能。两个示例处理大小差异的方式不同(一个升高,另一个停止)。
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]