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
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
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
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