From: Rick DeNatale Date: 2006-09-12T02:55:24+09:00 Subject: Re: Struggling with Blocks On 9/10/06, Paul Lutus wrote: > 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 => # 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. -- Rick DeNatale My blog on Ruby http://talklikeaduck.denhaven2.com/