Hal Fulton wrote:
> Paul Lutus wrote:
>> "do" reads from a list of items and provides them to its controlling
>> block:
> Not strictly true. Reemember that the iteration comes from the fact
> that the each method is invoking the block multiple times.
Yes, you are right (an I am quite wrong in the above quotation). The "do ...
end" block is simply a closure with an alternate syntax, and the calling
method provides the data and any repetition that may exist.
---------------------------------------------------
#! /usr/bin/ruby
def my_funct
1.upto(8) do |x|
yield x
end
end
my_funct do |item|
puts item
end
(or, equivalently)
my_funct { |item|
puts item
}
So it appears that the syntactic role played by "do" is that it receives and
acts on the data fed to it by the calling method, once (barring any
repetitions internal to the do ... end block). IOW it is a closure block,
nothing more. Any looping is in the hands of the calling method.
While it is true in that in the most common case of do..end (or {..}),
i.e. when used as an argument to an iterator method, there are
argument involved, it's not necessarily so.
It's a syntactic 'feature' of Ruby that a do..end can only appear in a
method call, well actually there are two exceptions, see below. The
method being called has other options with what it can do with the
block than yielding a series of values to it.
irb(main):001:0> to_s_proc = lambda do |arg| arg.to_s; end
=> #<Proc:0xb7dd99f8@(irb):1>
irb(main):002:0> to_s_proc.call(1)
=> "1"
In this case lamba takes the block literal delimited by do..end and
turns it into a proc. The proc can later be called by anyone with a
reference to it. An example of such a use might be to implement a
'call-back' in a GUI framework, where the proc would be something to
be called, say when a button was pushed, although in this case a proc
with a side-effect is more likely to be used. Note that there's no
necessity for such a 'call-back' proc to take any arguments.
And it's possible to use a proc passed in on method call in a later iterator
irb(main):003:0>[1, 2, 1.3].map(&to_s_proc)
=> ["1", "2", "1.3"]
Now for those exceptions I noted above.
until boolean_expression do
body
end
and
while boolean_expression do
body
end
These both execute body either until or while boolean_expression
evaluates to true. In these two cases do..end are purely syntactic
entities, no block is created.
Another construct might LOOK like an exception:
for name [,name]... in expression do
body
end
But it really isn't. Here the do..end does delimit a proc, and this
construct acts as if it were:
expression.each do | name [,name]... |
body
end
except for one little quirk, in this case any local variables defined
in body will be accessible outside the loop.
So it appears the only difference between "do ... end" and "begin ... end"
is that "begin ... end" doesn't receive data from a caller.
Now we've just seen three cases where do doesn't receive data at all.
So, what's the real deal with BEGIN..END. What it really does is to
delimit a series of statements which are treated like an anonymous
method which is called in place. In other words when the begin is
entered the equivalent of a execution context is placed on the call
stack. It's this which enables the error handling. The rescue
clauses get associated with this call context.
When an exception is raised, the call stack gets scanned for a context
which contains a matching rescue clause.
So a more accurate statement of the difference between do..end, and
begin..end is that entering a begin..end stacks an execution context,
as does entering a method, whereas do..end does not.
And that's why you can put rescue clauses in a method definition, or
in a begin..end expression, but not directly in do..end.
···
On 9/10/06, Paul Lutus <nospam@nosite.zzz> wrote:
--
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/