正如@Sergio 所说,它主要用于链接,但不止于此。如果您有枚举器e,则可以使用Enumerator#next 和Enumerator#peek 提取元素。以下是枚举器如何发挥优势的两个示例。
问题:给定一个数组a,构造另一个数组,如果i 为奇数,则索引i 的值为a[i],如果i 为偶数,则为2*a[i]。假设a = [1,2,3,4]。
人们通常会看到:
a.map.with_index { |n,i| n.odd? ? n : 2*n } #=> [1,4,3,8]
但也可以这样写:
e = [1,2].cycle #=> #<Enumerator: [1, 2]:cycle>
a.map { |n| e.next * n } #=> [1, 4, 3, 8]
问题:给定一个数组a,将相等的连续值分块到数组中。让我通过展示它通常是如何完成的来使这个陈述更准确。假设a = [1,1,2,3,3,3,4]。
a.chunk(&:itself).map(&:last) #=> [[1, 1], [2], [3, 3, 3], [4]]
在 Ruby v2.2(#itself 首次亮相)中,您可以使用 Enumerable#slice_when:
a.slice_when { |f,l| f != l }.to_a
#=> [[1, 1], [2], [3, 3, 3], [4]]
但您也可以使用枚举器:
e = a.to_enum
#=> #<Enumerator: [1, 1, 2, 3, 3, 3, 4]:each>
b = [[]]
loop do
n = e.next
b[-1] << n
nxt = e.peek
b << [] if nxt != n
end
b
#=> [[1, 1], [2], [3, 3, 3], [4]]
请注意,当n 是e 的最后一个值时,e.peek 将引发StopInteration 异常。该异常由Kernel#loop 通过跳出循环来处理。
我不建议使用最后一种方法优先于我提到的其他两个选项,但在其他情况下可以有效地使用这种方法。
还有一件事:如果你有一个链式方法的表达式,你可以通过将枚举器转换为数组来检查其元素被传递给块的枚举器的内容。从那里您可以看到需要哪些块变量。假设你想写:
[1,2,3,4].each_with_index.with_object({}) {....}
并在块中做一些事情,但不确定如何表达块变量。你可以这样做:
e = [1,2,3,4].each_with_index.with_object({})
#=> #<Enumerator: #<Enumerator: [1, 2, 3, 4]:each_with_index>
:with_object({})>
e.to_a
#=> [[[1, 0], {}], [[2, 1], {}], [[3, 2], {}], [[4, 3], {}]]
这表明(比如说)传递给块的e 的第一个元素是:
[[1, 0], {}]
告诉使用块变量应该是:
(n,i), h = [[1, 0], {}]
n #=> 1
i #=> 0
h #=> {}
表示应该写成表达式:
[1,2,3,4].each_with_index.with_object({}) { |(n,i),h|....}