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

Mike S. 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
http://www.cybersprocket.com

Mike S. 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}

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}

class MyArray

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

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

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 24.09.2007 05:42, David B. Williams wrote:

end

Hi Michael,
class MyArray
end
end

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