Rootroute ruby language pages |
An iterator is a method which accepts a block or a Proc
object. In the source file, the block is placed immediately after the
invocation of the method. Iterators are used to produce user-defined
control structures--especially loops.
Let's look at an example to see how this works. Iterators are often used to repeat the same action on each element of a collection, like this:
data = [1, 2, 3] data.each do |i| print i, "\n" end Produces: 1 2 3 |
The each
method of the array data
is passed the
do...end
block, and executes it repeatedly. On each call, the
block is passed successive elements of the array.
You can define blocks with {
...}
in place of
do
...end
.
data = [1, 2, 3] data.each { |i| print i, "\n" } Produces: 1 2 3 |
This code has the same meaning as the last example. However, in some
cases, precedence issues cause do
...end
and
{
...}
to act differently.
foobar a, b do .. end # foobar is the iterator. foobar a, b { .. } # b is the iterator. |
{...}
binds more tightly to the preceding expression
than does a do
block. The first example is equivalent to
foobar(a, b) do...
, while the second is foobar(a, b
{...})
.
You simply place the block after the iterator call. You can also pass
a Proc
object by prepending & to the variable or constant
name that refers to the Proc
.
There are three ways to execute a block from an iterator method: (1)
the yield
control structure; (2) calling a Proc
argument (made from a block) with call
; and (3) using
Proc.new
followed by a call.
The yield
statement calls the block, optionally passing it
one or more arguments.
def myIterator yield 1,2 end myIterator { |a,b| puts a, b } Produces: 1 2 |
If a method definition has a block argument (the last formal parameter
has an ampersand (&) prepended), it will receive the attached
block, converted to a Proc
object. This may be called using
method.call(args...)
.
def myIterator(&b) b.call(2,3) end myIterator { |a,b| puts a, b } Produces: 2 3 |
Proc.new
(or the equivalent proc
or lambda
calls), when used in an iterator definition, takes the
block which is given to the method as its argument, generates a
procedure object from it. (proc
and lambda
are
effectively synonyms.)
def myIterator Proc.new.call(3,4) proc.call(4,5) lambda.call(5,6) end myIterator { |a,b| puts a, b } Produces: 3 4 4 5 5 6 |
Perhaps surprisingly, Proc.new
and friends do not in any
sense consume the block attached to the method--each call to
Proc.new
generates a new procedure object out of the same
block.
You can tell if there is a block associated with a method by calling
block_given?
.
Proc.new
without a block do?
Proc.new
without a block cannot generate a procedure object
and an error occurs. In a method definition, however,
Proc.new
without a block implies the existence of a block
at the time the method is called, and so no error will occur.
Matz's solution, in [ruby-talk:5252], uses threads:
require 'thread' def combine(*args) queues = [] threads = [] for it in args queue = SizedQueue.new(1) th = Thread.start(it, queue) do |i,q| self.send(it) do |x| q.push x end end queues.push queue threads.push th end loop do ary = [] for q in queues ary.push q.pop end yield ary for th in threads return unless th.status end end end public :combine def it1 () yield 1; yield 2; yield 3 end def it2 () yield 4; yield 5; yield 6 end combine('it1','it2') do |x| # x is (1, 4), then (2, 5), then (3, 6) end |