Ruby comes with a built-in Enumerator class, which implements external iterators in Ruby.ruby
to_enum
方法,從而生成 Enumerator objectrubya = [1,3,"car"] b = { dog: "canine", fox: "vulpine" } # Create Enumerators enum_a = a.to_enum emum_h = h.to_enum enum_a.next # => 1 emum_h.next # => [:dog, "canine"] enum_a.next # => 3 enum_a.next # => [:fox, "vulpine"]
rubya = [1,3,"cat"] enum_a = a.each enum_a.next # => 1 enum_a.next # => 3
When an enumerator object runs out of values inside a loop, the loop will terminate cleanly.ide
rubyshort_enum = [1,2,3].to_enum long_enum = ('a'..'z').to_enum loop do puts "#{short_enum.next} - #{long_enum.next}" end produces: 1 - a 2 - b 3 - c
Enumerators take something that's normally executable code and turn it into an object.oop
This invokes its hose class's each method, returning successive values with an indexui
ruby['a', 'b', 'c'].each_with_index { |item, index| result << [item, index] } result # => [["a",0], ["b",1], ["c",2]]
The each_char method of strings will return an enumerator is you don't give it a blockcode
result = [] "cat".each_char.each_with_index {|item, index| result << [item, index]} result # => [["c", 0],["a",1],["t",2]]
each_with_index
也能夠簡寫爲 with_index
若是對象是 Enumeratororm
We can call to_a on an enumerator to iterate over it對象
rubyenum = "car".enum_for(:each_char) enum.to_a # => ["c","a","t"]
若是方法支持參數,也能夠傳進去three
rubyenum_in_threes = (1..10).enum_for(:each_slice, 3) enum_in_threes.to_a # => [[1,2,3],[4,5,6],[7,8,9],[10]]
rubytriangular_numbers = Enumerator.new do |yielder| number = 0 count = 1 loop do number += count count += 1 yielder.yield number end end 5.times { print triangular_numbers.next, " " } puts # => 1 3 6 10 15
rubytriangular_numbers = Enumerator.new do |yielder| number = 0 count = 1 loop do number += count count += 1 yielder.yield number end end p triangular_numbers.first(5) # => [1, 3, 6, 10, 15]
rubytriangular_numbers = Enumerator.new do |yielder| number = 0 count = 1 loop do number += count count += 1 yielder.yield number end end def infinite_select(enum, &block) Enumerator.new do |yielder| enum.each do |value| yielder.yield(value) if block.call(value) end end end p infinite_select(triangular_numbers) { |val| val 5 10 == 10 }.first(5)
None of the lazy versions of the methods actually consume any data from the collection until that data is requested.ci
rubydef Integer.all Enumerator.new do |yielder, n: 0| loop { yielder.yield(n += 1) } end.lazy end p Integer.all.first(10) # => [1,2,3,4,5,6,7,8,9,10] p Integer.all.select {|i| (i % 3).zero?}.first(10) # => [3,6,9,12,15,18,21,24,27,30]