Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

given/when one case prefixes another

by John M. Dlugosz (Monsignor)
on Apr 26, 2011 at 06:45 UTC ( #901284=perlquestion: print w/replies, xml ) Need Help??

John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

Using Perl 5 given/when construct (and hopefully available in 5.10.1), I have several cases that end the same way. Is there a proper way to "continue" to a different case and execute that one as if it had been selected?

Replies are listed 'Best First'.
Re: given/when one case prefixes another
by davido (Cardinal) on Apr 26, 2011 at 07:00 UTC

    perlsyn to the rescue:

    Fall-through

    You can use the continue keyword to fall through from one case to the next:

    given($foo) { when (/x/) { say '$foo contains an x'; continue } when (/y/) { say '$foo contains a y' } default { say '$foo does not contain a y' } }

    Note: I'm looking in perlsyn from 5.012_002.


    Dave

Re: given/when one case prefixes another
by AnomalousMonk (Bishop) on Apr 26, 2011 at 08:36 UTC

    This works in Strawberry 5.10.1.4, but I don't know how 'proper' it is; frankly, it looks a bit hinky to me.

    >perl -wMstrict -lE "for my $s qw(a b c d) { given ($s) { when ('a') { say 'a'; continue; } when ('b') { say 'b'; continue; } when (/[ab]/) { say ' after a or b'; } say 'neither a nor b'; when ('d') { say 'd'; } default { say qq{other: '$_'}; } } } " a after a or b b after a or b neither a nor b other: 'c' neither a nor b d

    BTW: I couldn't get the goto approach to work: Perl experiences a 'panic: goto' attack.

      It seems like perfectly fine code to me.

      I guess you are wondering if the code directly in "given" (without a "when" clause) is "proper". The answer is yes. That's one feature that distinguishes given/when from the switch statement in C.

      But do consider that such code could become hard to read if the whole given/when doesn't fit onto one or two screen pages anymore - you shouldn't be doing very much in the when clauses (calling functions or methods should be fine).

        I suppose I was uneasy about the possibility of the  when (/[ab]/) { ... } tail clause 'getting lost', dependent as it is on a  continue statement in other, possibly distant, clauses, and on the relative positions of those clauses (also a dependency of Neighbour's approach). Taking apl's suggestion, I am more comfortable with something like the following, which encapsulates the tail of each 'tailed' clause within the clause itself:

        >perl -wMstrict -lE "for my $s qw(a b c d) { given ($s) { my $tail_a_and_b = sub { say qq{ after a or b, was '$_' } }; when ('a') { say 'a'; $tail_a_and_b->(); } when ('b') { say 'b'; $tail_a_and_b->(); } when ('d') { say 'd'; } default { say qq{other: '$_'}; } } } " a after a or b, was 'a' b after a or b, was 'b' other: 'c' d
Re: given/when one case prefixes another
by ikegami (Pope) on Apr 26, 2011 at 06:53 UTC
    If you want to continue into the next block, you can use continue. If you want to go to some other block, you can use goto, although jumping into another block is deprecated.
      The post by Neighbor shows that continue doesn't jump to the next body at all, but continues looking for another match.

      I didn't consider GOTO into the block to be a solution, though someone might show how to label the outside or something.

        Oops on continue. Iw as going by the docs, and they aren't clear in that area, so I naturally assumed C-like behaviour.

        Labeling the outside of a block statement is easy and legal.

        FOO: { ... }
Re: given/when one case prefixes another
by Neighbour (Friar) on Apr 26, 2011 at 08:12 UTC
    You could modify $_ inside the given-when scope to some magic value indicating the common ending code needs to be executed. Like so:
    #!/usr/bin/perl use warnings; use strict; use v5.10; my @data = ('foo', 'bar', 'baz'); for my $element (@data) { given ($element) { when ('foo') { # Do foo things print("foo here\n"); # force common ending routine $_ = 'MAGIC_foobar'; continue; } when ('bar') { # Do bar things print("bar here\n"); # force common ending routine $_ = 'MAGIC_foobar'; continue; } when ('baz') { # Do baz things print("baz here\n"); } when ('MAGIC_foobar') { # Do common ending things here print("foobar ending here\n"); } } }
    This produces the following output:
    foo here foobar ending here bar here foobar ending here baz here
      Ah, so continue means "keep checking for more matches"? The docs, like the previous poster, said it falls through to the next case. That's not at all the same thing!

        Maybe "not the same thing" but definitely not "'keep checking for more matches'" (within the current when, anyway).

        At least, not in the same sense as the /g modifier in a regex. Observe:

        #!/usr/bin/perl use strict; use warnings; use 5.012; # 901284 my $count=0; my $foo = "X x y y x y Z"; say "First, with a simple substitution '/g'"; my $simplecount = $foo =~ s/x/o/g; say " \$foo: $foo; \n \$simplecount: $simplecount (i.e., 'x's changed) +"; $foo =~ s/o/x/g; say "\$foo restored: $foo \n"; say 'Now, with given/when and =~ /x/g'; given( $foo ) { when ( $foo =~ /x/g ) { say "\$foo contains an x"; $count++; say "\$foo: $foo; \n \$count: $count;"; continue } when ( /y/ ) { say "\n \$foo contains a y"; } default { say "$foo does not contain a y"; } } say "\n Final \$count of 'x' in given/when matching 'x': $count"; say "\n $foo";

        OUTPUT:

        First, with a simple substitution '/g' $foo: X o y y o y Z; $simplecount: 2 (i.e., 'x's changed) $foo restored: X x y y x y Z Now, with given/when and =~ /x/g $foo contains an x $foo: X x y y x y Z; $count: 1; $foo contains a y Final $count of 'x' in given/when matching 'x': 1 X x y y x y Z

        One possible reading is that continue's precedence terminates the matching. Perhaps a wiser head will explain (and correct, as necessary).

        Update: typo fixed (from /s/g to /x/g) at line 15 and in the output. Gnahh!

Re: given/when one case prefixes another
by LanX (Cardinal) on Apr 26, 2011 at 10:15 UTC
    Understanding the benefit of Given/When ... shows how to use for/if/next to mimic given/when/continue but defaulting to fall through.

    plz check perldelta if you wanna know about version dependencies. You're a monsignor, you have the licence to google...

    Cheers Rolf

      Good point about mixing if/when. I'm still not used to it enough to use it to its full ability.
Re: given/when one case prefixes another
by apl (Monsignor) on Apr 26, 2011 at 10:31 UTC
    I have several cases that end the same way.
    Turn the same way into a procedure, and refactor your code...
      The short amount of code and large about of local variables involved makes that awkward.

      I did end up changing the code, separating out the common stuff by making pre-tests before all that, so it was even more uniform.

        > The short amount of code and large about of local variables involved makes that awkward.

        why?

        for ( 1..2,"a".."b") { ifnum(); ifchar(); print "\n" } sub ifnum { if (/1/) { print } elsif (/2/) { print } else { return } print " is number"; } sub ifchar { print && goto CONT if /a/; print && goto CONT if /b/; return; CONT: print " is character"; }

        OUTPUT:

        1 is number 2 is number a is character b is character

        Cheers Rolf

        UPDATE: changed ifnum() to elsif-clauses.

        see also: Re: given/when one case prefixes another

Re: given/when one case prefixes another
by LanX (Cardinal) on Apr 26, 2011 at 22:07 UTC
    without goto and given/when

    Bare blocks are internally one-time while-loops allowing continue-blocks (see perlsyn)

    for ("a".."c"){ { print && next if /a/; print && next if /b/; # here neither a nor b last; # skip continue-block } continue { # here either a or b print " is in group1"; } print "\n" # all }

    your free to have more blocks or combining with elseifs or LABEL:s.

    Cheers Rolf

    UPDATE:

    or without fall-through

    for ("a".."c"){ { if ( /a/ ) { print } elsif ( /b/ ) { print } else { last } print " is in group"; # a,b } print "\n" # all }

    See also: Re^3: given/when one case prefixes another

      I see: there are advantages to using if statements instead of when's. I'll keep that in mind.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://901284]
Approved by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (8)
As of 2020-11-25 08:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?