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

Succinct switch statement

by Anonymous Monk
on Oct 23, 2009 at 20:26 UTC ( #802971=perlquestion: print w/replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I want my program to do different things depending on whether user input is a number, a string of only letters, and so on. So I try to make some sort of switch statement. But of course I want it to look really succinct and perly. I could do
while(<>){ if(/.../){...} elsif(/.../){...} elsif(/.../){...} }
or, using the fancy new switch feature,
while(<>){ given($_){ when(...){...} when(...){...} when(...){...} }}
Altho the given-when thing is cool, it would be cooler if the given() could be left out. It seems like the kind of thing that should be possible, because it's kind of, well, a given. Apparently it can be left out in a for() loop, but not in while(). And you can't use a for() loop in this case. Or can you?

And, by the way, is there an even shorter way to do print "\n"; ?

Replies are listed 'Best First'.
Re: Succinct switch statement
by jakobi (Pilgrim) on Oct 23, 2009 at 20:30 UTC

    E.g the code I've used for ages for flexible switch parsing, as the original simple arg handling module went against my tastes:

    # maybe replace @ARGV below with my(@A)=@ARGV or similar, as we destro +y the array while($_=$ARGV[0]){ /^(-V|-?-verbose)$/o and do{shift; $verbose=1; ...; next}; /^(-v|-?-negate)$/o and do{shift; $negate=1; ...; next}; ... /^--?$/o and do{shift; ...; last}; # early + loop exit last; }
    cu & HTH, Peter -- hints may be untested unless stated otherwise; use with caution & understanding.

    Update 1:

    Considering Bloodnok's variant below:

    If your Perl's sufficiently recent, use my($_) instead of local to protect your caller while also protecting from anything called functions might do.

    Also note that I'm using both next and last (loop exit condition) above. If you want a stronger distinction between looping and switching while retaining early loop exiting, you need either a second target label before the while() or set an explicit loop-exit flag variable for testing after the switch block. Simply saying last SWITCH in case of -- when restructuring does introduce a bug and changes semantics of -- to a NOP, with additional problems further down the line for non-option args.

    Footnotes to my example above:

    • For a cleaner syntax, you can use for my $_ (@ARGV) instead of the while if your loop nevers needs to access/eat multiple array members per iteration.
      But don't attempt the parens you normally should use with local, my and our: for my($_) (@ARGV)... will fail.
    • To fall through, you can either set $_ suitably or unshift one or more values.
    • Note the structural similarity of the lines above to, say, the suggestions in the mass pattern matching thread (->qr//, closures) or dispatch table methods (e.g. removing next, saying last whileloop depending on method-rc, and assuming NOP as default method)

    Non-famous last words: I simply prefer the above solution over given/when and if-cascades for readability and perceived elegance. But given the ascii art readjustment and byte counts, it's not faster to maintain nor that much shorter.

      Whilst your code self-evidently works, it doesn't satisfy the general case (pun intended) since it will only work within a loop - with modifications, it can be made to work anywhere.

      The modifications involves the enclosing of the condiitons and actions inside a labelled block and reworking the next statements e.g.

      while ($_ = $ARGV[0]) { SWITCH: { /^(-V|-?-verbose)$/o and do{shift; $verbose=1; ...; last SWITCH +}; /^(-v|-?-negate)$/o and do{shift; $negate=1; ...; last SWITCH +}; ... /^--?$/o and do{shift; ...; last SWITCH +}; } }
      Also, personally, I would rewrite it as follows
      PARSE: while (@ARGV) { local $_ = shift; SWITCH: { /^(-V|-?-verbose)$/o and do { $verbose=1; ...; last SWITCH }; /^(-v|-?-negate)$/o and do { $negate=1; ...; last SWITCH }; ... /^--?$/o and do { ...; last PARSE }; } }

      Added correct loop termination (using PARSE label) for specific instance offered above.

      A user level that continues to overstate my experience :-))
Re: Succinct switch statement
by bichonfrise74 (Vicar) on Oct 23, 2009 at 21:39 UTC
Re: Succinct switch statement
by keszler (Priest) on Oct 23, 2009 at 22:21 UTC
    And, by the way, is there an even shorter way to do print "\n"; ?
    Two characters shorter:
    print $/;
    Update: And very, very slightly slower:
    [keszler@tek perl]# cat #!/usr/bin/perl -w use Benchmark; use strict; my $results = timethese( 1e4, { a => sub{print "\n" for(1..1e4);}, b => sub{print $/ for(1..1e4);}, } ); my $results2 = timethese( 1e4, { a2 => sub{print $/ for(1..1e4);}, b2 => sub{print "\n" for(1..1e4);}, } ); [keszler@tek perl]# ./ | grep -v '^$' Benchmark: timing 10000 iterations of a, b... a: 54 wallclock secs (33.83 usr + 0.92 sys = 34.75 CPU) @ 28 +7.77/s (n=10000) b: 54 wallclock secs (34.21 usr + 0.96 sys = 35.17 CPU) @ 28 +4.33/s (n=10000) Benchmark: timing 10000 iterations of a2, b2... a2: 54 wallclock secs (34.37 usr + 0.93 sys = 35.30 CPU) @ 28 +3.29/s (n=10000) b2: 54 wallclock secs (33.69 usr + 0.90 sys = 34.59 CPU) @ 28 +9.10/s (n=10000)
      And very, very slightly slower:

      You just benchmarked I/O; you're not going to get meaningful results here.

Re: Succinct switch statement
by JadeNB (Chaplain) on Oct 24, 2009 at 01:56 UTC

    I don't know how to get rid of given, but you could make it look a little less redundant:

    while (1) { given (<>) { when () {} default { last } } }
    I think it would look prettier as
    do { given (<>) { when () {} default { last } } } while 1;
    but that gets you chastised about using last outside a loop block.

    As others have observed, say '' is the same as print "\n". Have you thought about whether setting $\ will fulfil your needs?

Re: Succinct switch statement
by moritz (Cardinal) on Oct 23, 2009 at 20:38 UTC
    Altho the given-when thing is cool, it would be cooler if the given() could be left out.

    It can. given just sets $_, and the outer while-loop does that already, so it's really redundant.

    And, by the way, is there an even shorter way to do print "\n"; ?


    Perl 6 - links to (nearly) everything that is Perl 6.
      No, it can't. You get error Can't use when() outside a topicalizer (5.10.1).
        I tested it with a for loop, which works. The difference seems to be that while doesn't limit the scope of $_ in any way.
      And say prints $_, doesn't it?
        And say prints $_, doesn't it?
        Do you see any mention of $_ in The Fine Manual? I don't. If you don't believe the documentation, you could always write your own code to answer your own question.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://802971]
Approved by SuicideJunkie
Front-paged by SuicideJunkie
[Corion]: Huh. I wasn't aware that you cannot segregate Python 2 and Python 3 modules... Hopefully Perl 6 module use C6AN and thus avoid the problem...
[hippo]: I very much hope so.
[moritz]: there are already some (very few) Perl 6 modules on CPAN, but in a way that the p5 indexer avoids them
[hippo]: It's confusing enough to have the Perl6:: namespace which isn't for Perl6 but for features from that ported into Perl.

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (10)
As of 2017-01-24 09:21 GMT
Find Nodes?
    Voting Booth?
    Do you watch meteor showers?

    Results (203 votes). Check out past polls.