Answer to your original question
[1,2,3].each.is_a?(Enumerable)
#=> true
[1,2,3].each.is_a?(Enumerator)
#=> true
[1,2,3].each.class.ancestors
#=> [Enumerator, Enumerable, Object, Kernel, BasicObject]
Yes, the "iterator" each
returns an Enumerator
when no block is used.
But if you're just learning Ruby and want to iterate over an Array/Range/Hash, just know that using each
will cover most of your cases :
[1, 2, 3].each do |element|
puts element
end
# 1
# 2
# 3
('a'..'e').each do |element|
puts element
end
# a
# b
# c
# d
# e
{'a' => 1, 'b' => 2}.each do |key, value|
puts key
puts value
end
# a
# 1
# b
# 2
At your level, you shouldn't have to care where those methods are defined, for which class or module or how they're called.
Finally, for loops
shouldn't be used in Ruby because they can show weird behaviours.
Your updated question
It's good that you made your question clearer. Note that the change might go unnoticed though, especially if you already accepted an answer.
3.times
3.times do |x|
puts x
end
enumerator = 3.times
enumerator.each do |x|
puts x
end
Used like this, both are perfectly equivalent. Since the second one is more verbose and enumerator
probably isn't used anywhere else, there's no reason to use the second variant. enumerator
is longer than 3.times
anyway :)
Note that |x|
should be on the same line as the block start. Rubocop could help you.
each_char
"scriptkiddie".each_char{|x| puts x}
"scriptkiddie".enum_for(:each_char).each{|x| puts x}
Again, no reason to use the 2nd variant if all you do is create an Enumerator and call each
directly on it.
Why use Enumerator?
Chaining methods
One reason to use an Enumerator
is to be able to chain Enumerable
methods :
puts 3.times.cycle.first(7)
#=> [0, 1, 2, 0, 1, 2, 0]
or
"script".each_char.with_index{|c, i|
puts "letter #{i} : #{c}"
}
# letter 0 : s
# letter 1 : c
# letter 2 : r
# letter 3 : i
# letter 4 : p
# letter 5 : t
Infinite lists
Enumerators also make it possible to work with infinite lists.
require 'prime'
every_prime = Prime.each
p every_prime.first(20)
#=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
p every_prime.lazy.select{|x| x > 1000}.first(3)
#=> [1009, 1013, 1019]
Custom iteration
It's possible to define new Enumerators for custom iterations :
require 'date'
def future_working_days(start_date = Date.today)
date = start_date
Enumerator.new do |yielder|
loop do
date += 1
yielder << date unless date.saturday? || date.sunday?
end
end
end
puts future_working_days.take(3)
# 2017-02-01
# 2017-02-02
# 2017-02-03