Newbie question: Enumerators

Discussion in 'Ruby' started by Omran Nazir, Nov 21, 2009.

  1. Omran Nazir

    Omran Nazir Guest

    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.=0A=0ACan someone ple=
    ase 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 s=
    o? What is the difference between and Enumerator and an Iterator?=0A=0AThan=
    ks in advance.=0A=0A Imran Nazir =0A=0AImran's profile" border=3D"0">=0AFri=
    end, Boho, House Owner, Citizen, Engineer=0A=0A=0A
    Omran Nazir, Nov 21, 2009
    #1
    1. Advertising

  2. Hi --

    On Sun, 22 Nov 2009, Omran Nazir 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

    --
    THE COMPLEAT RUBYIST, Ruby training with Black/Brown/McAnally!
    January 22-23, Tampa, Florida
    Info and registration at http://www.thecompleatrubyist.com
    --------------------------------------
    My new job: http://tinyurl.com/yfpn9hz
    David A. Black, Nov 21, 2009
    #2
    1. Advertising

  3. On Saturday 21 November 2009 03:35:03 pm Omran Nazir 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?
    David Masover, Nov 21, 2009
    #3
  4. Omran Nazir 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'
    --
    Posted via http://www.ruby-forum.com/.
    Brian Candler, Nov 22, 2009
    #4
  5. Omran Nazir

    Omran Nazir Guest

    Thanx for responses, you guys are champions!=0A=0A Imran Nazir =0A=0A=0AFri=
    end, Boho, House Owner, Citizen, Engineer=0A=0A=0A=0A=0A=0A________________=
    ________________=0AFrom: Omran Nazir <>=0ATo: ruby-t=
    alk ML <>=0ASent: Sat, 21 November, 2009 21:35:03=0A=
    Subject: Newbie question: Enumerators=0A=0AHi, I know this is a very newbie=
    question but its one concept that despite google and the O'Riley book I ca=
    nt get my head around.=0A=0ACan someone please give a simple explantion of =
    what exactly an Enumerator is, what it can act on and why you can pass 'arr=
    ay.to_enum' to a method that expect 'array'? What sort of objects can be de=
    fined as 'enumerable' and what makes them so? What is the difference betwee=
    n and Enumerator and an Iterator?=0A=0AThanks in advance.=0A=0AImran Nazir =
    =0A=0AImran's profile" border=3D"0">=0AFriend, Boho, House Owner, Citizen, =
    Engineer=0A=0A=0A
    Omran Nazir, Nov 22, 2009
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Matt Taylor
    Replies:
    6
    Views:
    425
    Victor Bazarov
    Jul 13, 2004
  2. Replies:
    6
    Views:
    4,158
    Pete Becker
    Apr 23, 2005
  3. Dave
    Replies:
    1
    Views:
    314
    Ron Natalie
    Nov 9, 2005
  4. Replies:
    5
    Views:
    108
  5. Hal Fulton

    Enumerators and generators

    Hal Fulton, Jul 22, 2010, in forum: Ruby
    Replies:
    7
    Views:
    118
    Roger Pack
    Jul 22, 2010
Loading...

Share This Page