Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

next and last within subs

by xorl (Deacon)
on Jul 11, 2014 at 18:54 UTC ( [id://1093287]=perlquestion: print w/replies, xml ) Need Help??

xorl has asked for the wisdom of the Perl Monks concerning the following question:

I'm looping through the contents of an array.

Before each iteration of the loop is completed, I want a couple of things done (increment a count, print some debug info, and a few other things). There are a number of conditions that cause the loop to move on to the next one early so my though is to put these into a subroutine and call that. However, one of these things is:

if ($x > $stop) { last; } elsif ($y ne $text) { foobar(); } else { next; }

So of course I get the warning "Exiting subroutine via last" (or next)

Is there a good way of doing this without having to put that snippet of code into every place where I need to move the loop forward?

Update: hexcoder with the continue block is exactly what I need. Thanks!

Replies are listed 'Best First'.
Re: next and last within subs
by Laurent_R (Canon) on Jul 11, 2014 at 20:43 UTC
    Why don't you exit the subroutine using return and let the calling procedure do the last, next or whatever, depending on what you return? Something like this:
    sub loop_control { if ($x > $stop) { return "last"; } elsif ($y ne $text) { foobar(); } else { return "next"; } } # ... my $return_status = loop_control(); last if $return_status eq "last"; next if $return_status eq "next"; # ...
    The alternative is to pass the array (or an array_ref) to the subroutine and loop through the array elements within the subroutine.

    In both cases, you avoid the warning (and avoid suppressing this warning) and you gain a better control on what to do, say, before jumping to the next element or exiting the loop altogether. For example, you could use a continue block or a redo, or you could change the two relevant lines above to print some debug statements:

    say "exiting the loop" and last if $return_status eq "last"; say "going to next element" and next if $return_status eq "next"; # ...
Re: next and last within subs
by hexcoder (Curate) on Jul 12, 2014 at 17:45 UTC
    I would avoid the subroutine and use continue like this:
    for my $entry (@somearray) { ... if ($x > $stop) { last; } elsif ($y ne $text) { foobar(); } else { next; } ... } continue { # increment a count # print some debug info # ...a few other things }
Re: next and last within subs
by toolic (Bishop) on Jul 11, 2014 at 19:38 UTC
    Could you show a little more context? Is that snippet in a sub which is called from a for loop?

    If you're really sure of what you're doing, you could locally disable the warnings (perllexwarn):

    use warnings; use strict; foo($_) for 1 .. 5; sub foo { no warnings 'exiting'; my $z = shift; print "$z\n"; last if $z > 2; } __END__ 1 2 3
      This is basically what i'm doing. The other problem with exiting the sub with next or last is that it seems to completely exit the loop when at least in two cases I want to just move on to the next iteration.
      foreach (@array) { if ($something) { # do stuff if ($something1) { # time to move to next iteration of loop before_next_stuff(); } } if ($somethingelse) { # do some other stuff before_next_stuff(); } # more stuff in loop before_next_stuff(); } sub before_next_stuff { # lots o stuff if ($x > $stop) { last; } elsif ($y ne $text) { foobar(); } else { next; } }
        Trying to control a loop from a subroutine called from inside the loop is just asking for trouble. It might seem like it will work... it might even work in some circumstances... but most likely it's just going to fail. Can you see how Perl is confused by your instructions? Basically, you're trying to use a subroutine for loop control when you should be doing some form of nesting, and leave loop control inside the loop:

        sub before_next_stuff { # lots o stuff } MAINJOB: foreach ( @array ) { if ( $something ) { # do stuff if ( $something_1 ) { # time to move to next iteration # of the loop before_next_stuff(); # you should only include this # loop control here if you must if ( $x > $stop ) { last MAINJOB; } elsif ( $y ne $text ) { foobar(); } else { next MAINJOB; } } } if ( $something_else ) { # do some other stuff before_next_stuff(); # you should only include this # loop control here if you must if ( $x > $stop ) { last MAINJOB; } elsif ( $y ne $text ) { foobar(); } else { next MAINJOB; } } # more stuff in loop before_next_stuff(); if ( $x > $stop ) { last MAINJOB; } elsif ( $y ne $text ) { foobar(); } # no "next" necessary here }


        A big part of effective and elegant programming is to structure your loops so that controlling them is mostly a matter of natural flow. However, there are times when "natural flow" is simply impossible, and you need some way of interrupting loop flow -- which is why Perl has next and last functions. But they should still be used as sparingly as possible.

        Bottom line: loop control should never be farmed out to a sub external to the loop. You can abstract everything else out into subs, but control needs to stay internal.

        PS- Any time I have nested or complex loops, I use explicit labels (like "MAINJOB", above). They help both Perl and me (much more me than Perl) keep track of which loop is being controlled at any given time.

        Sounds like you want to return from the sub, rather than try to manipulate the loop outside the sub.

Re: next and last within subs
by RonW (Parson) on Jul 11, 2014 at 20:08 UTC

    I would do something like:

    sub foo { # do things return (1 and (complex condition)) } while (main condition) { # do things last if foo; # do more things }
Re: next and last within subs
by BillKSmith (Monsignor) on Jul 12, 2014 at 04:09 UTC
    The following three lines are equivalent to your subroutine. Use them each place they are needed.
    last if $x > $stop; next if $y eq $text; foobar();
    Bill

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1093287]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2024-04-23 23:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found