枚举器什么时候有用?

时间:2015-05-29 16:36:22

标签: ruby enumerator

可以通过调用某些方法而不传递块来创建Enumerator对象,例如Array#reject

(1..3).reject.to_a
# => [1, 2, 3]

这只是一些红宝石规则,更容易链接,还是有其他方法将行为传递给枚举器?

3 个答案:

答案 0 :(得分:4)

  

这只是一些红宝石规则,更容易链接

是的,这个原因完全正确。轻松链接普查员。考虑这个例子:

ary = ['Alice', 'Bob', 'Eve']

records = ary.map.with_index do |item, idx|
  {
    id: idx,
    name: item,
  }
end

records # => [{:id=>0, :name=>"Alice"}, {:id=>1, :name=>"Bob"}, {:id=>2, :name=>"Eve"}]

map会将每个元素都生成with_index,这会将项目索引置于其顶部并生成块。阻止将值返回with_index,返回map做其事,映射和)返回给调用者。

答案 1 :(得分:1)

  

这只是一些红宝石规则,更容易链接

这不是Ruby的规则。只是在没有块的情况下调用reject方法(例如)返回Enumerator。它可以做其他事情。

  

还是有其他方式将行为传递给Enumerator

是的,Enumerator封装了您创建Enumerator的方法的行为。例如,从Enumerator创建reject会创建一个拒绝其某些元素的对象。 Enumerator混合了Enumerable,因此Enumerator可以与其他任何Enumerable进行混合。{1}}。例如:

enum = (1..3).reject

enum.with_index {|el, i| i.even? }
# => [2]

答案 2 :(得分:1)

正如@Sergio所说,它主要用于链接,但它超越了这一点。如果您有一个枚举器e,则可以使用Enumerator#nextEnumerator#peek提取元素。以下是两个如何利用枚举器的例子。

问题:给定数组a,构造另一个数组,如果i为奇数且a[i],其索引i的值为2*a[i] 1}}如果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|....}