Loop/Iterator questions

  1. Is there anything like Perl’s continue block available? This is
    not next, but a block that gets executed on each loop, before the
    condition is tested, similar to the third block of a for loop does.
    Ex:
    i = 10
    while i > 0
    # do stuff
    x = rand
    if x > 0.5 next
    p "not always get here"
    continue
    i -= 1
    end

In the above example, i -= 1 gets executed at the end of the while
loop or if next is called.

  1. Is there anything like labels for iterators or loops? This allows
    you to do next, last, etc. on outside loops, like:

      loop1:   while x > 1
                    while y > 1
                        if t2 then next loop1
                    end
               end
    

This could be emulated with a flag and an ugly if check, but I hope
not.

  1. Is there anything like Perl’s continue block available?

Not that I’m aware of, although I could easily be mistaken.
But Ruby’s looping primitives are in general rather, well,
primitive, since most of the time an iterator method is used
instead.

  1. Is there anything like labels for iterators or loops? This allows
    you to do next, last, etc. on outside loops, like:

      loop1:   while x > 1
                    while y > 1
                        if t2 then next loop1
                    end
               end
    

This could be emulated with a flag and an ugly if check, but I hope
not.

That sort of nonlocal control flow is done with catch/throw in Ruby.

    catch(:done)  do
        while x > 1
            while y > 1
                if t2 then throw :done
            end
        end
    end

Catch and throw are explicitly designed for this sort of thing, unlike
raise/rescue which are for genuine error conditions.

-Mark

···

On Thu, Jan 01, 2004 at 08:55:54PM -0800, GGarramuno wrote:

GGarramuno wrote:

  1. Is there anything like Perl’s continue block available? This is
    not next, but a block that gets executed on each loop, before the
    condition is tested, similar to the third block of a for loop does.
    Ex:
    i = 10
    while i > 0
    # do stuff
    x = rand
    if x > 0.5 next
    p “not always get here”
    continue
    i -= 1
    end

In the above example, i -= 1 gets executed at the end of the while
loop or if next is called.

I like that feature… I’d never heard of that before! You could do
something similar in Ruby like this:

i = 10

while i > 0
begin
x = rand
next if x > 0.5
puts “#{i} not always get here”
ensure
i -= 1
end
end

···


Jamis Buck
jgb3@email.byu.edu

ruby -h | ruby -e ‘a=;readlines.join.scan(/-(.)[e|Kk(\S*)|le.l(…)e|#!(\S*)/) {|r| a << r.compact.first };puts “\n>#{a.join(%q/ /)}<\n\n”’

  1. Is there anything like Perl’s continue block available? This is not
    next, but a block that gets executed on each loop, before the
    condition is tested, similar to the third block of a for loop does. Ex:
    i = 10
    while i > 0
    # do stuff
    x = rand
    if x > 0.5 next
    p “not always get here”
    continue
    i -= 1
    end

In the above example, i -= 1 gets executed at the end of the while loop
or if next is called.

This oughta do it:

i = 10
while i > 0
begin
# do stuff
if (x = rand) > 0.5 then next
puts “not always get here”
ensure
i -= 1
end
end

Gavin

irb(main):006:0> 10.downto(1) do |i|
irb(main):007:1* p i
irb(main):008:1> next if rand > 0.5
irb(main):009:1> p “not always reached”
irb(main):010:1> end

Alternatively:

irb(main):025:0> while i > 0
irb(main):026:1> begin
irb(main):027:2* p i
irb(main):028:2> next if rand > 0.5
irb(main):029:2> p “not always reached”
irb(main):030:2> ensure
irb(main):031:2* i -= 1
irb(main):032:2> end
irb(main):033:1> end

I’m not sure you need either condition, really, as Ruby’s iteratorrs
tend to be far better in most circumstances.

-austin

···

On Fri, 2 Jan 2004 13:56:45 +0900, GGarramuno wrote:

  1. Is there anything like Perl’s continue block available? This is
    not next, but a block that gets executed on each loop, before the
    condition is tested, similar to the third block of a for loop
    does. Ex:
    i = 10
    while i > 0

    do stuff

x = rand
if x > 0.5 next
p “not always get here”
continue
i -= 1
end

In the above example, i -= 1 gets executed at the end of the while
loop or if next is called.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.01.02
* 01.07.23

Austin Ziegler austin@halostatue.ca wrote in message news:20041211319.014174@PADD

I’m not sure you need either condition, really, as Ruby’s iteratorrs
tend to be far better in most circumstances.

-austin

Well, the idea of the continue block is to do more than a single
decrementing of a variable (else, indeed an iterator should indeed
suffice). My example was just a dummy one.
The ensure example and the use of catch address both perl features
perfectly with the equivalent rubyisms.

  1. Is there anything like labels for iterators or loops? This allows
    you to do next, last, etc. on outside loops, like:

      loop1:   while x > 1
                    while y > 1
                        if t2 then next loop1
                    end
               end
    

This could be emulated with a flag and an ugly if check, but I hope
not.

That sort of nonlocal control flow is done with catch/throw in Ruby.

    catch(:done)  do
        while x > 1
            while y > 1
                if t2 then throw :done
            end
        end
    end

Hmm… that does not seem quite correct, thou.

The throw() example above actually breaks from both loops. That’s not
the same as next, thou.

Given pseudo-code, like:

  catch(:arg) do
    while $stdin.getc != "a"
       begin                     
           p "A"                    
           while 1
                 p "B"
                 throw :arg if 1
           end
       ensure
           #do a lot of stuff here
           p "ensure"
       end
    end
  end

In the dummy example above, I want the loop to continue (thus, getting
a character, and printing “A”,“B”,“ensure” until “a” is typed).
The throw seems to just exit the loop. If the catch is placed inside
the while, the getc condition is not run, then.

What’s the right way?

I think that my point is that I’ve more or less eliminated “normal”
loops from my Ruby. They just don’t feel right. (There are some in
PDF::Writer, but again, I haven’t had the time to clean that.)

There are a few other places where I use while loops, but I think
that if I put a little thought into it, I wouldn’t need them,
either.

-austin

···

On Sat, 3 Jan 2004 03:01:46 +0900, GGarramuno wrote:

Austin Ziegler austin@halostatue.ca wrote in message

I’m not sure you need either condition, really, as Ruby’s
iterators tend to be far better in most circumstances.
Well, the idea of the continue block is to do more than a single
decrementing of a variable (else, indeed an iterator should indeed
suffice). My example was just a dummy one. The ensure example and
the use of catch address both perl features perfectly with the
equivalent rubyisms.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.01.02
* 13.53.09

        while $stdin.getc != "a"

getc return a Fixnum, not a String

Guy Decoux

ts decoux@moulon.inra.fr wrote in message news:200401041500.i04F0jg04445@moulon.inra.fr

    while $stdin.getc != "a"

getc return a Fixnum, not a String

Guy Decoux

Thanks for mentioning that. That could indeed be some future gotcha I
could run into.

Still, that is not dealing with the flow problem I have.

Testing it now with:

catch(:arg) do
while $stdin.getc
begin
p “A”
while 1
p “B”
throw :arg if 1
end
ensure
#do a lot of stuff here
p “ensure”
end
end
end

and I still exit the loop right away. I want to be able to do a
“next” on the outer loop from within the inner loop and the
throw/catch mechanism seems to be only a solution to breaking out of
loops, not iterating to the next step (while evaluating the
condition).
I’m sure I am missing some obvious way of doing this.

GGarramuno wrote:

catch(:arg) do
while $stdin.getc
begin
p “A”
while 1
p “B”
throw :arg if 1
end
ensure
#do a lot of stuff here
p “ensure”
end
end
end

and I still exit the loop right away. I want to be able to do a
“next” on the outer loop from within the inner loop and the
throw/catch mechanism seems to be only a solution to breaking out of
loops, not iterating to the next step (while evaluating the
condition).
I’m sure I am missing some obvious way of doing this.

I think you just need to put the catch(:arg) inside the outer loop, like
this:

while $stdin.getc
catch(:arg) do
begin
p “A”
while true
p “B”
throw :arg if true
end
ensure
#do a lot of stuff here
p “ensure”
end
end
end

Another thing to watch for – avoid treating ‘1’ as true. Although ‘1’
is true, so is ‘0’, in Ruby. Use the ‘true’ and ‘false’ literals, instead.

···


Jamis Buck
jgb3@email.byu.edu

ruby -h | ruby -e ‘a=;readlines.join.scan(/-(.)[e|Kk(\S*)|le.l(…)e|#!(\S*)/) {|r| a << r.compact.first };puts “\n>#{a.join(%q/ /)}<\n\n”’

Thanks for mentioning that. That could indeed be some future
gotcha I could run into.

Still, that is not dealing with the flow problem I have.

Testing it now with:

catch(:arg) do
while $stdin.getc
begin
p “A”
while 1
p “B”
throw :arg if 1
end
ensure
# do a lot of stuff here
p “ensure”
end
end
end

As Jamis said, you want:

while $stdin.getc
begin
catch(:arg) do
p “A”
while true
p “B”
throw :arg if true
end
end
ensure
p “ensure”
end
end

The catch block wraps what you want to be caught; in your original
code, you completely exit the loop.

Remember as well, that only nil and false are false in Ruby; that
means 0 is a true value.

and I still exit the loop right away. I want to be able to do a
“next” on the outer loop from within the inner loop and the
throw/catch mechanism seems to be only a solution to breaking out
of loops, not iterating to the next step (while evaluating the
condition). I’m sure I am missing some obvious way of doing this.

As Guy pointed out, $stdin.getc returns an integer, not a character,
but you can get integer values with ?: ?a, ?b, ?:, ?!, etc.

catch(:outer) do
while s = $stdin.getc
print s.chr # Converts integer to character
begin
catch(:inner) do
print “A”
while true
print “B”
throw :inner if true
end
end
ensure
print “E”
end
throw :outer if s == ?q
end
end

-austin

···

On Mon, 5 Jan 2004 03:06:48 +0900, GGarramuno wrote:

austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.01.04
* 14.40.58

Austin Ziegler austin@halostatue.ca wrote in message news:200414145957.997798@PADD

As Jamis said, you want:

while $stdin.getc
begin
catch(:arg) do
p “A”
while true
p “B”
throw :arg if true
end
end
ensure
p “ensure”
end
end

Hmm…
Well, that’s what I thought at first, but I had quickly discarded the
idea as not right.
Yet, it does work. I am obviously missing some other gotcha here.

In the above example, by placing the catch where it is, the way I read
it was that $stdin.getc gets run only once, instead of in each
iteration (ie. the while condition is not evaluated as it is OUTSIDE
the catch do/end, just as is the ensure block)? This is what’s
loosing me. What’s the logic -magic- that makes it all work?

Or to put in another way, what if I wanted to have a catch() that is
inside the first while loop but does not evaluate the condition nor
runs the ensure block (say, just to backtrace the code like an ugly
goto command)? How would that look like? Or that’s just not possible
(and thus it explains why the above thing always gets interpreted the
way we intended)?

while $stdin.getc
begin
catch(:arg) do
p “A”
while true
p “B”
throw :arg if true
end
end
ensure
p “ensure”
end
end

Well, that’s what I thought at first, but I had quickly discarded the
idea as not right.
Yet, it does work. I am obviously missing some other gotcha here.

In the above example, by placing the catch where it is, the way I read
it was that $stdin.getc gets run only once, instead of in each
iteration (ie. the while condition is not evaluated as it is OUTSIDE
the catch do/end, just as is the ensure block)? This is what’s
loosing me. What’s the logic -magic- that makes it all work?

There are TWO loops. The throw breaks out of the INNER loop, but the
OUTER loop continues on its merry way.

-Mark

···

On Sun, Jan 04, 2004 at 05:28:02PM -0800, GGarramuno wrote:

Or to put in another way, what if I wanted to have a catch() that is
inside the first while loop but does not evaluate the condition nor
runs the ensure block (say, just to backtrace the code like an ugly
goto command)? How would that look like? Or that’s just not possible
(and thus it explains why the above thing always gets interpreted the
way we intended)?

GGarramuno wrote:

In the above example, by placing the catch where it is, the way I read
it was that $stdin.getc gets run only once, instead of in each
iteration (ie. the while condition is not evaluated as it is OUTSIDE
the catch do/end, just as is the ensure block)? This is what’s
loosing me. What’s the logic -magic- that makes it all work?

“catch” is just a method. All it does is evaluate the block that you
attach to it. If, “inside” of that block (ie, directly, or via methods
called from within the block), the “throw” method is called (note that
it is also a method), AND if the symbol passed to the throw method
matches the one for the given catch method, THEN the catch method
terminates, returning the (optional) value passed to the throw method.

catch(:some_symbol) do
do_something
do_something_else
catch(:another_symbol) do
do_yet_another_thing
throw :some_symbol
end
end

In the above example, the ‘throw’ passes control back to the outermost
catch invocation, which terminates.

Or to put in another way, what if I wanted to have a catch() that is
inside the first while loop but does not evaluate the condition nor
runs the ensure block (say, just to backtrace the code like an ugly
goto command)? How would that look like? Or that’s just not possible
(and thus it explains why the above thing always gets interpreted the
way we intended)?

There is no way (that I know of) to ‘throw’ within a protected region
(ie, one inside of a ‘begin’…‘end’, with rescue clauses and an ensure
clause) without executing the associated ensure block. ‘catch’ is very
much like a goto, but it makes sure that ‘ensure’ clauses are all
executed on its way out.

Hope that helps…kind of.

  • Jamis
···


Jamis Buck
jgb3@email.byu.edu

ruby -h | ruby -e ‘a=;readlines.join.scan(/-(.)[e|Kk(\S*)|le.l(…)e|#!(\S*)/) {|r| a << r.compact.first };puts “\n>#{a.join(%q/ /)}<\n\n”’