Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ending an iteration of a loop from a subroutine

I have a program with a while loop which has several points where certain conditions require some action to be taken and then the remainder of the iteration to be skipped.

As this is always going to be the same code, I wanted to put it in a subroutine, but when I try using 'next;' as the final statement in the sub I get a warning (Exiting subroutine via next at ...), although it appears to work as intended.

i.e. without the sub:

while (#condition) {

    ## do stuff

    if (#condition to skip iteration) {
        ## action
        next;
    }

    ## do more stuff, with the above block repeated several times
}

with sub:

while (#condition) {

    ## do stuff

    &skip if (#condition to skip iteration);

    ## do more stuff, with more calls to &skip
}

sub skip() {
    ## action
    next;
}

The rest of the code in the block/sub is so short that putting all but the next; statement in a sub pretty much defeats the object of using a subroutine.

My question is:

  • Is using 'next;' in a subroutine in this way ok, or is it dangerous/poor practice?
  • Is there a better way to skip the rest of the iteration from a subroutine?
  • If it is ok and there isn't a better way, is there a way to suppress the warning?
like image 455
CarrieVS Avatar asked Sep 12 '14 12:09

CarrieVS


Video Answer


2 Answers

It's easy to suppress the warning. Just place the following line above your next:

no warnings "exiting";

The warning is there for a reason - using next in a helper sub like that can be confusing for the next person who has to read the code (who might be you in 6 months' time!) because the next does not occur lexically within the loop block. If you're just reading the loop block, you might not notice that next further down the file; and if you're just reading the definition of the subroutine, you might not be sure what that next is for. You need to read both parts of the code together to make sense of it. That makes it more confusing than using next directly inside the loop.

Also, it limits the re-usabilty of the skip() sub you've just defined. Want to re-use that skip() sub inside another loop? You'd better hope that the skip logic still makes sense in the new loop.

If you've considered all that and still want to go ahead, then just disable the warning as I showed above. Warnings are not errors, they're just warnings. That's why warnings are called "warnings". They're designed to bring your attention to something potentially problematic; not to stop you from doing something you've decided is useful.

like image 114
tobyink Avatar answered Sep 23 '22 18:09

tobyink


From the Learning perl 6th edition page 179 (footnote)

It’s probably not a good idea, but you could use these loop-control operators from inside a subroutine to control a loop that is outside the subroutine. That is, if a subroutine is called in a loop block, and the subroutine executes last when there’s no loop block running inside the subroutine, the flow of control will jump to just after the loop block in the main code. This ability to use loop control from within a subroutine may go away in a future version of Perl, and no one is likely to miss it.

The solution could be what others already said, or you can do additional test in the sub, like

use strict;
use warnings;

while(<>) {
    chomp;
    maybeskip($_) && next if m/2/; #maybe skip if match 2
    print "$_\n";
}

sub maybeskip {
    $_[0] !~ m/0/; #skip only if not match 0
    # the sub retuns the result of the last executed expression
    # if this is not wanted you should use explicit return $value;
}

for the

seq 25 | perl script

prints:

1
3
4
5
6
7
8
9
10
11
13
14
15
16
17
18
19
20

e.g. skipped all matched 2 but not 20

like image 38
jm666 Avatar answered Sep 21 '22 18:09

jm666