How to add an iterator to a class?

I have a simple class (this is just an example, so ignore any syntax or
style errors):

class MyArray
    def initialize
        @a = Array.new
    end
    def Push( item )
        @a.push( item )
    end
    def GetArray
        return @a
    end
end

And I have this code:

myarray = MyArray.new
myarray.Push( 1 )
myarray.Push( 2 )

myarray.GetArray.each do | i |
    puts i
end

So my question is: How do I replace GetArray with something more elegant,
that gives MyArray a method named .each that I can call directly?

Michael Steiner

class MyArray
  def each
    @a.each do |x|
      yield x
    end
  end
end

Though you may be better off simply extending Array and just add custom
functionality - Array is pretty good at implementing push and each.

You may also want to check out the Enumerable mixin if you're going to play
with collections of objects and don't want to inherit.

HTH,

Felix

···

-----Original Message-----
From: Mike Steiner [mailto:mikejaysteiner@gmail.com]
Sent: Sunday, September 23, 2007 7:14 PM
To: ruby-talk ML
Subject: how to add an iterator to a class?

I have a simple class (this is just an example, so ignore any
syntax or
style errors):

class MyArray
    def initialize
        @a = Array.new
    end
    def Push( item )
        @a.push( item )
    end
    def GetArray
        return @a
    end
end

And I have this code:

myarray = MyArray.new
myarray.Push( 1 )
myarray.Push( 2 )

myarray.GetArray.each do | i |
    puts i
end

So my question is: How do I replace GetArray with something
more elegant,
that gives MyArray a method named .each that I can call directly?

Michael Steiner

class MyArray
    def initialize
        @a = ["hey", "whats", "up!"]
    end

    def each(&block)
        @a.each &block
    end
end
m = MyArray.new
m.each{|el| p el} # => "hey"
                                       "whats"
                                        "up!"

here we are simply reimplementing the each function of the array class, if
the need is to implement it functionally,
then we should use the "yield" explicitly.

···

On 9/24/07, Mike Steiner <mikejaysteiner@gmail.com> wrote:

I have a simple class (this is just an example, so ignore any syntax or
style errors):

class MyArray
    def initialize
        @a = Array.new
    end
    def Push( item )
        @a.push( item )
    end
    def GetArray
        return @a
    end
end

And I have this code:

myarray = MyArray.new
myarray.Push( 1 )
myarray.Push( 2 )

myarray.GetArray.each do | i |
    puts i
end

So my question is: How do I replace GetArray with something more elegant,
that gives MyArray a method named .each that I can call directly?

Michael Steiner

--
sur
"is a String object" is a String object
hacking over objects...
http://expressica.com

Mike Steiner wrote:

I have a simple class (this is just an example, so ignore any syntax or
style errors):

class MyArray
    def initialize
        @a = Array.new
    end
    def Push( item )
        @a.push( item )
    end
    def GetArray
        return @a
    end
end

And I have this code:

myarray = MyArray.new
myarray.Push( 1 )
myarray.Push( 2 )

myarray.GetArray.each do | i |
    puts i
end

So my question is: How do I replace GetArray with something more
elegant,
that gives MyArray a method named .each that I can call directly?

Michael Steiner

Hi Michael,

What you want to do is mixin the Enumerable module into your class:

http://www.ruby-doc.org/core/classes/Enumerable.html

You then have to define an #each method that yields for each member of
your collection.

For example:

class MyArray
  include Enumerable

  def initialize
    @a = Array.new
  end

  def each
    for e in @a do
      yield e
    end
  end
end

From there, your class will have all of the methods defined in
Enumerable.

David B. Williams

···

--
Posted via http://www.ruby-forum.com/\.

Mike Steiner wrote:

So my question is: How do I replace GetArray with something more
elegant,
that gives MyArray a method named .each that I can call directly?

each() is an "iterator". An iterator is a method that uses a "yield
statement". A yield statement acts similar to a method call, and looks
like this:

yield some_val

yield calls the block and sends it the argument specified to the right
of 'yield'. The method that contains the yield statement, i.e. each(),
halts and waits for the block to finish executing, and then execution
continues in the method. That interaction is very similar to a method
that calls another method:

def greet
    sleep(2)
    puts "hello"
    sleep(5)
end

def start
    greet

    x = 10
    puts x
end

start

If you run that code, you'll see that the method start() waits for
greet() to finish executing before it outputs 10. yield works
similarly: execution does not continue in the method that contains the
yield statement, e.g. each(), until the block that the yield statement
called finishes executing.

So, you need to define a method called each, and inside each you are
going to use a yield statement that sends values to a block. What
values? The values are the elements of your array. Since you are
generally going to need to yield more than one value, i.e. your array
will have more than one element in it, you can put your yield statement
inside a loop:

class MyArray
    def initialize
        @a = Array.new
    end
    def push( item )
        @a.push( item )
    end

    def each
        count = 0
        while elmt = @a[count]
            yield elmt
            count += 1
        end
    end

end

arr = MyArray.new
arr.push(1)
arr.push(2)
arr.push(3)

arr.each{|elmt| puts elmt +2}

···

--
Posted via http://www.ruby-forum.com/\.

Here's a super simple example of an iterator:

def test
    yield
    yield
    yield

    puts "all done in test"
end

test {puts "hello"; sleep(2)}

In that example, test() calls the block but does not send it any
arguments--just like you can call a method and not send it any
arguments. Note that the block is not defined to accept any arguments.

When the first yield statement is encountered, the block is called. How
does the yield statement know what block to call? yield calls the block
specified to the right of the test method call. What if you call test
and don't specify a block?

r4test.rb:2:in `test': no block given (LocalJumpError)
        from r4test.rb:9

After the first yield statement in test is encountered, execution halts
at the point of the first yield statement until the block outputs
"hello" and finishes sleeping. Then execution continues in test. The
next line in test is another yield statement, so the block is called
again, and execution halts in test until the block finishes. The same
thing happens a third time. Finally, test outputs its terminating
message.

Blocks can also return values to a yield statement--just like a method
can return a value to a method call, e.g.

def get_calc
    10 * 2
end

def do_homework
    answer = get_calc #return value replaces the method call in the
code
    puts answer
end

do_homework

Here is how a block returns a value to a yield statement:

def test
    result = yield 10 #catch the value returned by the block in a
variable

    puts result
end

test {10 * 2}

···

--
Posted via http://www.ruby-forum.com/.

In this simple case (i.e. without filtering or converting) you can even do this:

def each(&b)
   @a.each(&b)
   self
end

Also note that conventionally #each returns self.

Kind regards

  robert

···

On 24.09.2007 05:42, David B. Williams wrote:

Mike Steiner wrote:

I have a simple class (this is just an example, so ignore any syntax or
style errors):

class MyArray
    def initialize
        @a = Array.new
    end
    def Push( item )
        @a.push( item )
    end
    def GetArray
        return @a
    end
end

And I have this code:

myarray = MyArray.new
myarray.Push( 1 )
myarray.Push( 2 )

myarray.GetArray.each do | i |
    puts i
end

So my question is: How do I replace GetArray with something more elegant,
that gives MyArray a method named .each that I can call directly?

Michael Steiner

Hi Michael,

What you want to do is mixin the Enumerable module into your class:

module Enumerable - RDoc Documentation

You then have to define an #each method that yields for each member of your collection.

For example:

class MyArray
  include Enumerable

  def initialize
    @a = Array.new
  end

  def each
    for e in @a do
      yield e
    end
  end
end

7stud -- wrote:

Here is how a block returns a value to a yield statement:

def test
    result = yield 10 #catch the value returned by the block in a
variable

    puts result
end

test {10 * 2}

Whoops. That should be:

def test
    result = yield 10

    puts result
end

test {|num| num * 2} #block is defined to accept an arg

···

--
Posted via http://www.ruby-forum.com/\.