tags:

views:

7471

answers:

6

I'm using this code to let the user enter in names while the program stores them in an array until they enter an empty string (they must press enter after each name):

people = []
info = 'a' # must fill variable with something, otherwise loop won't execute

while not info.empty?
    info = gets.chomp
    people += [Person.new(info)] if not info.empty?
end

This code would look much nicer in a do ... while loop:

people = []

do
    info = gets.chomp
    people += [Person.new(info)] if not info.empty?
while not info.empty?

In this code I don't have to assign info to some random string.

Unfortunately this type of loop doesn't seem to exist in Ruby. Can anybody suggest a better way of doing this?

+1  A: 

Originally written by Jeremy Voorhis. The content has been copied here because it seems to have been taken down from the originating site. Copies can also be found in the Web Archive and at Ruby Buzz Forum. -Bill the Lizard

I found the following snippet while reading the source for Tempfile#initialize in the Ruby core library:

begin
  tmpname = File.join(tmpdir, make_tmpname(basename, n))
  lock = tmpname + '.lock'
  n += 1
end while @@cleanlist.include?(tmpname) or
  File.exist?(lock) or File.exist?(tmpname)

At first glance, I assumed the while modifier would be evaluated before the contents of begin...end, but that is not the case. Observe:

>> begin
?>   puts "do {} while ()" 
>> end while false
do {} while ()
=> nil

As you would expect, the loop will continue to execute while the modifier is true.

>> n = 3
=> 3
>> begin
?>   puts n
>>   n -= 1
>> end while n > 0
3
2
1
=> nil

While I would be happy to never see this idiom again, begin...end is quite powerful. The following is a common idiom to memoize a one-liner method with no params:

def expensive
  @expensive ||= 2 + 2
end

Here is an ugly, but quick way to memoize something more complex:

def expensive
  @expensive ||=
    begin
      n = 99
      buf = "" 
      begin
        buf << "#{n} bottles of beer on the wall\n" 
        # ...
        n -= 1
      end while n > 0
      buf << "no more bottles of beer" 
    end
end
hubbardr
:::: dead link.
wilhelmtell
Courtesy of the Wayback Machine: http://web.archive.org/web/20080206125158/http://archive.jvoorhis.com/articles/2007/06/13/ruby-hidden-do-while-loop
bta
This is why when linking to an external site, I always make sure to copy the relevant info into my answer here.
davr
+37  A: 

Like this:

people = []

begin
  info = gets.chomp
  people += [Person.new(info)] if not info.empty?
end while not info.empty?

Reference: http://archive.jvoorhis.com/articles/2007/06/13/ruby-hidden-do-while-loop

Blorgbeard
heh, beaten to it. damn.
Blorgbeard
Won't this code add an empty string to the array if there is no input?
AndrewR
That was my mistake, that was how my code was before I quickly edited it.
yjerem
It's easy to make mistakes in the rush for rep - I had to edit my answer twice before it was right :)
AndrewR
+15  A: 

How about this?

people = []

until (info = gets.chomp).empty?
  people += [Person.new(info)]
end
AndrewR
Nice, much more elegant.
Blorgbeard
But this isn't "do ... while" loop. :)
Alexander Prokofyev
But it does the same thing in this case, unless I'm mistaken
Blorgbeard
@Blorgbeard, a do..while loop always runs once, then evaluates to see if it should continue running. A traditional while/until loop can run 0 times.It isn't a HUGE difference, but they are different.
Scott S.
@Scott, that's true - I just meant that this code is equivalent to the OP's, even though it doesn't use a do/while. Although really, this code does half of the loop's "work" in the condition, so it's not quite a traditional while loop either - if the condition doesn't match, some work is still done.
Blorgbeard
+1  A: 
ppl = []
while (input=gets.chomp)
 if !input.empty?
  ppl << input
 else
 p ppl; puts "Goodbye"; break
 end
end
is_that_okay
+7  A: 

Here's the full text article from hubbardr's dead link to my blog.

I found the following snippet while reading the source for Tempfile#initialize in the Ruby core library:

begin
  tmpname = File.join(tmpdir, make_tmpname(basename, n))
  lock = tmpname + '.lock'
  n += 1
end while @@cleanlist.include?(tmpname) or
  File.exist?(lock) or File.exist?(tmpname)

At first glance, I assumed the while modifier would be evaluated before the contents of begin...end, but that is not the case. Observe:

>> begin
?>   puts "do {} while ()" 
>> end while false
do {} while ()
=> nil

As you would expect, the loop will continue to execute while the modifier is true.

>> n = 3
=> 3
>> begin
?>   puts n
>>   n -= 1
>> end while n > 0
3
2
1
=> nil

While I would be happy to never see this idiom again, begin...end is quite powerful. The following is a common idiom to memoize a one-liner method with no params:

def expensive
  @expensive ||= 2 + 2
end

Here is an ugly, but quick way to memoize something more complex:

def expensive
  @expensive ||=
    begin
      n = 99
      buf = "" 
      begin
        buf << "#{n} bottles of beer on the wall\n" 
        # ...
        n -= 1
      end while n > 0
      buf << "no more bottles of beer" 
    end
end

jvoorhis
+1  A: 
a = 1
while true
  puts a
  a += 1
  break if a > 10
end
Paul Gillard