Perl vs. Ruby breaking from nested loops

Breaking out of nested blocks is apparently done much differently in Perl and Ruby. In Perl if you run this:

my $counter = 0;
foreach my $outer (1..5) {
    foreach my $inner ('A'..'E') {
        print "$outer: $inner\n";
        last if ++$counter > 4
    }
}

The last is going to break out of the inner-most foreach loop by default. If you want to break out of the OUTER foreach loop you can use line labels:

my $counter = 0;
OUTER: foreach my $outer (1..5) {
    foreach my $inner ('A'..'E') {
        print "$outer: $inner\n";
        last OUTER if ++$counter > 4
    }
}

In Ruby to do the same thing, you have to use a throw/catch block. This is a completely separate mechanism from Ruby's begin/rescue Exception-handling mechanism. throw/catch rather appears to be simply a flow-control mechanism. You raise Exceptions, but you throw Strings or Symbols. The equivalent of the above Perl in Ruby:

counter = 0
catch :OUTER do
    1.upto(5) do |outer|
        'A'.upto'E' do |inner|
            puts "#{outer}: #{inner}"
            throw :OUTER if (counter += 1) > 4
        end
    end
end

EDIT: Originally I thought that Perl's last LABEL; would not find labels except in the same static scope. It must've been a bug in my test code however, because this does work. My mistake! So this does work in Perl:

sub test {
    HERE: foreach my $really_outer (1..5) {
        test2();
    }
}
 
sub test2 {
    my $counter = 0;
    foreach my $outer (1..5) {
        foreach my $inner ('A'..'E') {
            print "$outer: $inner\n";
            last HERE;
        }
    }
}
 
test

You can do this in Ruby also. The other nice thing about Ruby is you can return a value from the throw:

def test
    result = catch :HERE do
        1.upto(5) do |really_outer|
            test2
        end
    end
 
    puts "RESULT: #{result}"
end
 
def test2
    counter = 0
    1.upto(5) do |outer|
        'A'.upto'E' do |inner|
            puts "#{outer}: #{inner}"
            throw :HERE, "YOINK" if (counter += 1) > 4
        end
    end
end
 
test

In Perl you also have the option of using Error.pm, but it looks horrendously hackish to me, as Perl code often does.

The only thing I can think of that's bad about Ruby is that it has two things which are really similar, one for Exceptions and one not. People coming from a different language may think that throw/catch is the Exception-handling mechanism when it's not. But Ruby still wins here.

Tags: ,

3 Responses to “Perl vs. Ruby breaking from nested loops”

  1. Quoth Dave:

    > # How do you break to HERE?????

    WTF??? How about “last HERE;”? RTFM.

  2. Quoth Brian:

    I was wrong, it does work in Perl. Thanks for pointing it out. You are incredibly rude, however. Please learn how to interact with other human beings in a polite manner. :)

  3. Quoth Slaven Rezić:

    Why you need Error.pm? You can do both with stock perl5, the quite ugly exception-style and using a
    return value from the throw:

    my $counter = 0;
    eval {
    foreach my $outer (1..5) {
    foreach my $inner ('A'..'E') {
    print “$outer: $inner\n”;
    die { message => “jump out!” } if ++$counter > 4
    }
    }
    };
    use Data::Dumper; print Dumper $@;

Leave a Reply

You can use these tags in comments (Note: HTML is automatically escaped inside <pre> tags, nowhere else, so if you post source code, put it in <pre>):

<pre lang="some_programming_language"> 
<em>
<strong>
<a href="url">

NOTE: Comments are automatically spam-filtered. If your comment fails to appear, it was likely munched by the filter. Try not to link-spam or post anything that looks like it was typed by a robot.