如何使用枚举器

时间:2013-12-05 08:26:00

标签: ruby enumeration enumerator

在Ruby Array Class文档中,我经常发现:

  

如果没有给出阻止,则返回枚举器。

为什么我不会将块传递给#map?我的用处是什么:

[1,2,3,4].map

而不是:

[1,2,3,4].map{ |e| e * 10 } # => [10, 20, 30, 40]

有人能告诉我一个使用这个枚举器的非常实用的例子吗?

6 个答案:

答案 0 :(得分:7)

好问题。

如果我们想要使用创建的枚举器执行多项操作,该怎么办?我们现在不想处理它,因为这意味着我们可能需要稍后创建另一个?

my_enum = %w[now is the time for all good elves to get to work].map # => #<Enumerator: ["now", "is", "the", "time", "for", "all", "good", "elves", "to", "get", "to", "work"]:map>

my_enum.each(&:upcase) # => ["NOW", "IS", "THE", "TIME", "FOR", "ALL", "GOOD", "ELVES", "TO", "GET", "TO", "WORK"]
my_enum.each(&:capitalize) # => ["Now", "Is", "The", "Time", "For", "All", "Good", "Elves", "To", "Get", "To", "Work"]

答案 1 :(得分:4)

当没有给出块时返回enumerable的功能主要用于将可枚举类中的函数链接在一起时。像这样:

abc = %w[a b c]
p abc.map.with_index{|item, index| [item, index]} #=> [["a", 0], ["b", 1], ["c", 2]]

编辑:

我认为我对这种行为的理解有点过于局限,无法正确理解Ruby的内部工作原理。我认为最重要的是要注意参数的传递方式与Procs相同。因此,如果传入一个数组,它将自动'splatted'(这个更好的词?)。我认为获得理解的最好方法是使用一些简单的函数返回枚举并开始试验。

abc = %w[a b c d]
p abc.each_slice(2)                #<Enumerator: ["a", "b", "c", "d"]:each_slice(2)>
p abc.each_slice(2).to_a           #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x| x}     #=> [["a", "b"], ["c", "d"]]
p abc.each_slice(2).map{|x,y| x+y} #=> ["ab", "cd"]
p abc.each_slice(2).map{|x,| x}    #=> ["a", "c"] # rest of arguments discarded because of comma.
p abc.each_slice(2).map.with_index{|array, index| [array, index]} #=> [[["a", "b"], 0], [["c", "d"], 1]]
p abc.each_slice(2).map.with_index{|(x,y), index| [x,y, index]}   #=> [["a", "b", 0], ["c", "d", 1]]

答案 2 :(得分:4)

Ruby核心库(EnumeratorArray)和标准库({{}中的Hash和大多数其他数据结构之间的主要区别1}},Set)是SortedSet可以无限。您不能拥有Enumerator所有偶数或零流或所有素数,但您绝对可以拥有Array

Enumerator

那么,你可以用这样的evens = Enumerator.new do |y| i = -2 y << i += 2 while true end evens.take(10) # => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] zeroes = [0].cycle zeroes.take(10) # => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 做什么?好吧,三件事,基本上。

  1. Enumerator混合在Enumerator中。因此,您可以使用所有Enumerable方法,例如Enumerablemapinjectall?any?none?select等等。请注意,reject可能是无限的,而Enumerator会返回map,因此尝试Array无限map可能会产生无限大Enumerator Array并且花费无限的时间。

  2. 有一些包装方法以某种方式“丰富”Enumerator并返回一个新的Enumerator。例如,Enumerator#with_index向块添加“循环计数器”,Enumerator#with_object添加备忘录对象。

  3. 您可以使用Enumerator,就像您在其他语言中使用Enumerator#next进行外部迭代一样,使用Enumerator方法,它会为您提供下一个值(并移动raise 1}}转发)或StopIteration Enumerator例外如果(1..1.0/0)是有限的并且您已到达终点。

  4. 例如,无限范围:{{1}}

答案 3 :(得分:2)

除了hirolau的回答之外,还有另一种方法lazy可以组合来修改枚举数。

a_very_long_array.map.lazy

如果map总是占用一个块,则必须有另一个方法,如map_lazy,类似地,其他迭代器也可以做同样的事情。

答案 4 :(得分:0)

Enumerator允许内部和外部迭代的类

=> array = [1,2,3,4,5]
=> array.map
=> #<Enumerator: [2, 4, 6, 8, 10]:map>
=> array.map.next
=> 2
=> array.map.next_values
=> [0] 2

答案 5 :(得分:0)

我猜有时你想将Enumerator传递给另一个方法,不管这个Enumerator来自哪个地方:mapslice,无论如何:

def report enum
  if Enumerator === enum
    enum.each { |e| puts "Element: #{e}" }
  else
    raise "report method requires enumerator as parameter"
  end
end

> report %w[one two three].map
# Element: one
# Element: two
# Element: three

> report (1..10).each_slice(2)    
# Element: [1, 2]
# Element: [3, 4]
# Element: [5, 6]
# Element: [7, 8]
# Element: [9, 10]