Newbie question: Enumerators

Hi, I know this is a very newbie question but its one concept that
despite google and the O’Riley book I cant get my head around.

Can someone please give a simple explantion of what exactly an
Enumerator is, what it can act on and why you can pass ‘array.to_enum’
to a method that expect ‘array’? What sort of objects can be defined as
‘enumerable’ and what makes them so? What is the difference between and
Enumerator and an Iterator?

Thanks in advance.

Imran Nazir

Imran’s profile" border=“0”>
Friend, Boho, House Owner, Citizen, Engineer

Hi –

On Sun, 22 Nov 2009, Omran N. wrote:

Hi, I know this is a very newbie question but its one concept that
despite google and the O’Riley book I cant get my head around.

It isn’t a very nuby question at all. Enumerators are a pretty
advanced topic!

Can someone please give a simple explantion of what exactly an
Enumerator is, what it can act on and why you can pass
‘array.to_enum’ to a method that expect ‘array’? What sort of
objects can be defined as ‘enumerable’ and what makes them so? What
is the difference between and Enumerator and an Iterator?

An Enumerator is an Enumerable object (it has all the Enumerable
methods, like map, inject, find, etc.). The difference between an
enumerator and most enumerable objects is that most enumerable objects
have a “natural” solution to the question of what items they are
enumerating (or iterating over). For an array, the natural solution is
that the items are the elements of the array. For a hash, it’s the
key/value pairs, and so on.

An enumerator knows about the Enumerable methods, but it doesn’t have
any natural or automatic sense of what to iterate through. Therefore,
it has to attach itself to another object – in fact, to a specific
method on another object.

Once you hook an enumerator up to a method on another object, the
enumerator will perform all of its enumerable operations (map, find,
etc.) by drawing on the output from that method.

Here’s a kind of artificial example that might show you the basic
workings:

class MyDemo
def yield_stuff
yield 1
yield 22
yield 333
yield 4444
end
end

md = MyDemo.new
e = md.to_enum(:yield_stuff)

p e.select {|x| x > 100 } # [333, 4444]

If you rename yield_stuff to each, and create the enumerator like
this:

e = md.to_enum

it will still work, because the default method the enumerator attaches
to is each.

Note that I would be able to pass my enumerator to any method that
expected an enumerable object, on which it could call select and other
enumerable methods.

I don’t think I’ve answered everything but hopefully that will get you
started.

David

On Saturday 21 November 2009 03:35:03 pm Omran N. wrote:

Hi, I know this is a very newbie question but its one concept that despite
google and the O’Riley book I cant get my head around.

Can someone please give a simple explantion of what exactly an Enumerator
is,

Something which provides several things you usually find in an
Enumerable
object, such as #each. Probably the simplest example of why you would
want one
is to take something that behaves like #each and use other fun tools
like
#map. A useless example – you already know what map does:

(1…10).map{|x| x*2}

Now you can combine it with other iterators:

(1…10).each_slice(2).map{|a,b| a+b}

If you want to see what each_slice is doing, try this:

(1…10).each_slice(2).to_a

Basically, each_slice is returning an enumerator which, instead of just
giving
us the numbers 1-10, it gives us the pairs of numbers – 1,2; then 3,4;
and so
on.

You can do other fun (but pointless) hacks like:

10.times.map{|x|x+1} == (1…10).to_a

Another reason you would want one is the ability to call enum.next
repeatedly
– to invert control, in a way. That is, rather than doing this:

File.open(‘foo’) do |file|
file.each_line do |line|
# do something with line
end
end

You could instead do this:

File.open(‘foo’) do |file|
enum = file.each_line
begin
loop do
line = enum.next
# do something with line
end
rescue StopIteration
# end of file
end
end

Why would you want to? Well, in case you wanted to do those reads out of
order
– basically, it lets you control when and how you read a line. Here’s
an
example that’s closer to being practical:

File.open(‘foo’) do |file|
enum = file.each_line
begin
loop do
line = enum.next.chomp
while line =~ /\$/
line = line.chop + enum.next.chomp
end
# do something with line…
end
rescue StopIteration
# end of file
end
end

Basically, this looks for any line that ends in a backslash and treats
that as
a continued line. But for that to work, we’re essentially having to read
ahead
– notice that second “enum.next” in there. File#each_line won’t work,
but a
technique like this might be useful to implement, say,
File#each_continued_line.

what it can act on

Anything that has an #each method, or any method that behaves similarly.
Look
up the documentation for Object#enum_for for an example. In fact, these
days,
whenever I write any sort of iterator, I make sure to return an enum if
I
don’t get a block. That’s how the above each_line works.

and why you can pass ‘array.to_enum’ to a method
that expect ‘array’?

Most methods don’t “expect” any particular type at all, probably only a
behavior. I don’t know what method you’re talking about, but they
probably
just expect something that implements ‘each’.

Google “duck typing” for more on this philosophy.

By the way, I may have told you way more than you ever wanted to know,
as
there’s absolutely no reason for passing ‘array.to_enum’ to a method
that
would’ve been happy with ‘array’, because ‘array.to_enum.each’ does
exactly
the same thing as ‘array.each’, only slightly less efficiently.

What sort of objects can be defined as ‘enumerable’
and what makes them so?

Well, again, see duck typing. There’s nothing special about making an
object
“enumerable”. The easiest way to do so is to include the Enumerable
module,
but no sane code would ever check that – if needed, you could rewrite
everything Enumerable does.

For a semi-formal definition, look up the documentation for that
Enumerable
module. Anything that includes that module can probably be considered
Enumerable. Anything that behaves like that module is by definition
Enumerable.

Note that the Enumerator class uses Enumerable.

What is the difference between and Enumerator and
an Iterator?

I’m not sure what an iterator is. I’m guessing it’s meant to be a method
like
each. Call it with a block, and it runs the block on each element of
itself.
Call it without a block, and it returns an Enumerator for itself.

Unless the word “iterator” is being used a lot, I wouldn’t worry too
much. I
only see it once in the definition – iterator? is an alias for
block_given?

Omran N. wrote:

What sort of objects can be defined as
‘enumerable’ and what makes them so?

Something is enumerable if it implements a method (conventionally called
“each”) which yields a series of values into a block. If you mix in the
Enumerable module then you get a whole bunch of additional methods which
build on this - select, map, min/max, sort_by etc. Each of those method
just calls your “each” method and then processes the values yielded.

Can someone please give a simple explantion of what exactly an
Enumerator is, what it can act on and why you can pass ‘array.to_enum’
to a method that expect ‘array’?

As it happens, someone asked me to write this up a few days ago. The
result is here:
http://wiki.github.com/rdp/ruby_tutorials_core/enumerator

It’s not something that most people need. The most common use is when
you want to use the Enumerable methods but iterate using a method other
than ‘each’, for example ‘each_byte’ or ‘each_with_index’

Thanx for responses, you guys are champions!

Imran Nazir

Friend, Boho, House Owner, Citizen, Engineer


From: Omran N. [email protected]
To: ruby-talk ML [email protected]
Sent: Sat, 21 November, 2009 21:35:03
Subject: Newbie question: Enumerators

Hi, I know this is a very newbie question but its one concept that
despite google and the O’Riley book I cant get my head around.

Can someone please give a simple explantion of what exactly an
Enumerator is, what it can act on and why you can pass ‘array.to_enum’
to a method that expect ‘array’? What sort of objects can be defined as
‘enumerable’ and what makes them so? What is the difference between and
Enumerator and an Iterator?

Thanks in advance.

Imran Nazir

Imran’s profile" border=“0”>
Friend, Boho, House Owner, Citizen, Engineer