Stephen White <spwhite / chariot.net.au> writes:

> On 24 Aug 2001 11:40:56 +0900, Dave Thomas wrote:
> >     addrFields = ["First Name", "Last Name", "Phone Number"]
> >     addrData = []
> >     loop do
> >       addrFields.each do |prompt|
> >         print "\n\t#{prompt}: "
> *         line = gets
> *         break if !line || line == "END\n"
> *         addrData << getLine.chop
> 
> Did you mean "line" instead of "getLine"?

Yes - I didn't catch them all in the copy :)

> 
> I think this is a bit nicer:
> 
>   line = gets ? $_.chomp : "END"
>   break if line == "END"
>   addrData << line

Perhaps, but I personally don't like the $_ or the duplication of
END. The specification of the problem really makes it hard to find a
neat solution for this. Another variant that avoids the $_ is

   END_FLAG = "END"

   # ...

   line = (gets || END_FLAG).chomp
   break if line == END_FLAG

If you want to emphasize clarity, there's

   break unless line = gets
   line.chomp!
   break if line == "END"

For speed you could do

   loop do
     begin
       addrField.each do |prompt|
         print prompt
         line = gets.chomp
         break if line == "END"
         addData << line
       end
     rescue NameError
       # terminate when chomp-ing nil at EOF
     end
     break unless addData.size == addField.size
     # etc...
   end



> While I cringe at breaking twice, this version does have less conceptual
> overhead than any alternatives I can think of.

Don't cringe - the two breaks are there for different reasons: that's
why I added the inner loop back in. The structure is

   loop  # reading addresses
     read_an_address
     write_it_out if a complete address was given
   end

where read_an_address is

   loop
     prompt
     read
     store
   end

That seems to me to be a more accurate reflection of the real world
than unfolding the two loops in to one with some magic flags. With the
two loop version, it would be easy to split the reading out, or to add
different printing semantics, both without risking interfering with
the rest of the logic. And, interestingly, the two loop version is
actually shorter.

Of all the solutions, I though the one based on collect was probably
the neatest, but it may be a bit obscure for an introductory column.


On a side note, this whole thread has been an interesting example of
the power of Ruby to communicate coding ideas. It has the high-level
expressiveness to allow us to document overall structuring ideas, but
at the same time it forces us to considered practical coding (such as
the nil-at-end-of-file problem). I like it!


Regards



Dave