http://www.perlmonks.org?node_id=388880

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

How can i make construction like this in perl ?

Warning - there are no break statements (this technique known as fall through).

For example if var = 10, "abcdefghij" will be printed and if var = 5, "fghij" will be printed and so on.

Thank you very much for help !

switch (var) {
case 10: printf("a");
case 9: printf("b");
case 8: printf("c");
case 7: printf("d");
case 6: printf("e");
case 5: printf("f");
case 4: printf("g");
case 3: printf("h");
case 2: printf("i");
case 1: printf("j");
}

Replies are listed 'Best First'.
Re: fall through switch/case in perl
by Velaki (Chaplain) on Sep 06, 2004 at 23:50 UTC

    If you really want the case statement, there are many ways of doing it, as per the Programming Perl, a.k.a. the "Camel" book.

    However, I think the following might be more what you need.

    print substr("abcdefghij",10-$var),"\n";

    Hope that helped,
    -v
    "Perl. There is no substitute."
      print splice @{[a..j]}, -$var

      or better yet:

      print( (a..j)[-$var..-1] )

Re: fall through switch/case in perl
by etcshadow (Priest) on Sep 07, 2004 at 00:27 UTC
    Well, what the first couple folks said doesn't actually answer your question... they were answering what *would* have been your question if you *had* had break statements... I don't know if the original post was updated after the fact or if they simply did not actually read it... whatever.

    The short answer is that there isn't anything that does this very well, and in the same way as C does. The thing to understand is that switch statements in C are actually just well-written GOTO statements! Likewise, the best way to do this might be GOTOs (as bad as that might sound)... or just coding it explicitly, something like:

    for ( $var ) { my $go; ($go || $_ == 10) and $go++, print "a"; ($go || $_ == 9 ) and $go++, print "b"; ($go || $_ == 8 ) and $go++, print "c"; ($go || $_ == 7 ) and $go++, print "d"; ($go || $_ == 6 ) and $go++, print "e"; ($go || $_ == 5 ) and $go++, print "f"; ($go || $_ == 4 ) and $go++, print "g"; ($go || $_ == 3 ) and $go++, print "h"; ($go || $_ == 2 ) and $go++, print "i"; ($go || $_ == 1 ) and $go++, print "j"; }
    ------------ :Wq Not an editor command: Wq
      Here's the computed goto version:
      eval { goto 'L'.($var+0) }; die "$var out of range"; L10: print "a"; L9: print "b"; L8: print "c"; L7: print "d"; L6: print "e"; L5: print "f"; L4: print "g"; L3: print "h"; L2: print "i"; L1: print "j"; print "\n";

        Shouldn't that be

        eval { goto 'L'.($var+0) }; goto L_default; L10: print "a"; L9: print "b"; L8: print "c"; L7: print "d"; L6: print "e"; L5: print "f"; L4: print "g"; L3: print "h"; L2: print "i"; L1: print "j"; L_default: print "\n";

        to preserve the semantics of the original code?

        Makeshifts last the longest.

        The eval isn't required (unless I missed a subtlety?)

        #! perl -sw use strict; my $var = 7; goto 'CASE'.($var+0); die "$var out of range"; CASE10: print "a"; CASE9: print "b"; CASE8: print "c"; CASE7: print "d"; CASE6: print "e"; CASE5: print "f"; CASE4: print "g"; CASE3: print "h"; CASE2: print "i"; CASE1: print "j"; print "\n"; __END__ P:\test>junk defghij

        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
Re: fall through switch/case in perl
by Aristotle (Chancellor) on Sep 07, 2004 at 01:03 UTC

    Warning: may cause nausea. Posted purely for hack value. An indent of 2 spaces used to protect the right margin from harm. Don't try this at home.

    for ( $var ) { $_ == 1 or do { $_ == 2 or do { $_ == 3 or do { $_ == 4 or do { $_ == 5 or do { $_ == 6 or do { $_ == 7 or do { $_ == 8 or do { $_ == 9 or do { $_ == 10 or do { last; }; print "a"; }; print "b"; }; print "c"; }; print "d"; }; print "e"; }; print "f"; }; print "g"; }; print "h"; }; print "i"; }; print "j"; }

    Makeshifts last the longest.

Re: fall through switch/case in perl
by TomDLux (Vicar) on Sep 07, 2004 at 01:41 UTC

    The inherent weakness in your query is that you are asking for a Perl equivalent to a basic 'switch' idiom, when Perl doesn't provide a 'switch' construct.

    You are asking about Duff's Device ..... which, as I remember, was invented to save a couple dozen machine instructions. My understanding is that Duff's Device is mostly viewed as poor programming, since saving a few dozen machine cycles at 3 GHz is less important than leading to quick and clear reader understanding of the code. On the other hand, I can imagine programmers dealing with highly-efficient code, such as embedded programmers, might accept Duff as an easily recognized idiom.

    So the next question is, without a switch statement, how would we implement Duff?

    We've had one suggestion, using substr(), very closely related to the details of this question.

    More generally, you might consider re-framing the conditionals:

    for my $val ( $var ) { print "a" if ( $val >= 10 ); print "b" if ( $val >= 9 ); print "c" if ( $val >= 8 ); print "d" if ( $val >= 7 ); print "e" if ( $val >= 6 ); print "f" if ( $val >= 5 ); print "g" if ( $val >= 4 ); print "h" if ( $val >= 3 ); print "i" if ( $val >= 2 ); print "j" if ( $val >= 1 ); }

    So, yes, it can be achieved, but you need to think somewhat differently.

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

      Actually, this is just a standard switch statement,-- not Duff's device.

      On the clarity argument, it's very much in the eye of the beholder whether your if cascade (or the and cascade) is clearer than the computed-goto version?

      For completeness, the computed goto version works out an average of over 60% faster than either of the cascade versions, which is rather more than a few dozen clock cycles.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

        That's because it doesn't test and retest conditions like the cascades do, it just computes a goto target.

        Makeshifts last the longest.

      1. The OP isn't asking about Duff's device.
      2. Your code isn't Duff's device either.
      3. Your code is inefficient (it's always doing 10 comparisons).
      4. Your code has a pointless for loop.
Re: fall through switch/case in perl (w/closure)
by demerphq (Chancellor) on Sep 07, 2004 at 07:08 UTC

    TIMTOWTDI.

    #!perl -l my $var=9; my @sub=(0,map { sub { print $_ } } reverse 'a'..'j'); if ($var>=0 and $var<=$#sub) { $sub[$_] && $sub[$_]->() for reverse 1..$var; }

    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi


Re: fall through switch/case in perl
by DamnDirtyApe (Curate) on Sep 07, 2004 at 07:12 UTC

    The first answer that came to mind was the Switch module, but for some reason it doesn't seem to be behaving as expected. Can anyone tell me why this doesn't do what it looks like it should do?

    #! /usr/bin/perl use strict; use Switch 'fallthrough'; sub test_switch { my $val = shift; switch ( $val ) { case 10 { print "a"; } case 9 { print "b"; } case 8 { print "c"; } case 7 { print "d"; } case 6 { print "e"; } case 5 { print "f"; } case 4 { print "g"; } case 3 { print "h"; } case 2 { print "i"; } case 1 { print "j"; } } } test_switch( 10 ); print "\n"; test_switch( 5 ); print "\n"; __END__

    Results:

    a f

    Maybe I'm overlooking something, but I'm failing to see why this isn't doing just what the OP asked for. Please help.


    _______________
    DamnDirtyApe
    Those who know that they are profound strive for clarity. Those who
    would like to seem profound to the crowd strive for obscurity.
                --Friedrich Nietzsche

      I looked at Switch first, too. The thing with Switch 'fallthrough' is that it falls to the next condition, not the next bit of code (like it does in C).

      If each successive condition is inclusive of all its predessors, you can do it with Switch 'fallthrough':

      use Switch 'fallthrough'; sub test_switch { my $val = shift; switch ( $val ) { case 10 { print "a"; } case [9..10] { print "b"; } case [8..10] { print "c"; } case [7..10] { print "d"; } case [6..10] { print "e"; } case [5..10] { print "f"; } case [4..10] { print "g"; } case [3..10] { print "h"; } case [2..10] { print "i"; } case [1..10] { print "j"; } } }

      This is hard to extend beyond the realm of successive integers, of course.

Re: fall through switch/case in perl
by Zed_Lopez (Chaplain) on Sep 07, 2004 at 05:36 UTC

    Here's another ugly but working way to do it:

    my %switch; @switch{1..10} = (qw(L1 L2 L3 L4 L5 L6 L7 L8 L9 L10)); goto END_SWITCH unless exists $switch{$var}; goto $switch{$var}; L10: print "a" ; L9: print "b" ; L8: print "c" ; L7: print "d" ; L6: print "e" ; L5: print "f" ; L4: print "g" ; L3: print "h" ; L2: print "i" ; L1: print "j" ; END_SWITCH: # whatever
Re: fall through switch/case in perl
by Anonymous Monk on Sep 07, 2004 at 08:42 UTC

    Thank you guys very much !

    I've found here many ways to do this switch.

    Especially for etcshadow for his original way.

    Thanks TimToady, Aristotle, BrowserUk for example with eval-ed goto (actually I was thinking abiut this way when i was wakin up today).

    Aristotle nested do works, but code looks very ugly ;)

    TomDLux does not solve fallthrugh case in general, but perfectly suits for my case (i choosed such solution, thank you).

    Very original solution gave me demerphq, but it not sutable for me because i'm doing some computation work (instead of prints, which I've chosed for example of how fall through works for people not familar with C).

    substr actually does not fit, because i've choosed print as example of fall through (as i said earlier).

    PS. I'm new to perlmonks, but my first impression is great !

    PPS. Does anybody knows where I can post tips at perlmonks (i've found a nice way to make addition and subtraction by modulo 2^32 on machines where perl compiled with use64bitint=undef)

      PPS. Does anybody knows where I can post tips at perlmonks (i've found a nice way to make addition and subtraction by modulo 2^32 on machines where perl compiled with use64bitint=undef)

      Register as a monk and post away

Re: fall through switch/case in perl
by Aristotle (Chancellor) on Sep 06, 2004 at 23:45 UTC
    for ( $var ) { $_ == 10 and print "a"; $_ == 9 and print "b"; $_ == 8 and print "c"; $_ == 7 and print "d"; $_ == 6 and print "e"; $_ == 5 and print "f"; $_ == 4 and print "g"; $_ == 3 and print "h"; $_ == 2 and print "i"; $_ == 1 and print "j"; }

    There are about ten million other ways to say the same, of course.

    Makeshifts last the longest.

      You missed his note about "fall through" ala C.

      In his example, a value of '5' would produce "fghij". A value of '2' would produce "ij". Velaki's code recognizes this better.

      But I wonder if he might ever have var set to zero? (No output!)

        No, I did not miss that note. Did you run the code?

        Makeshifts last the longest.

      Mhm wonder what happens if $var is 20000000000 or -1.

      PerlingTheUK

        Nothing.

        Makeshifts last the longest.

Re: fall through switch/case in perl
by jeffa (Bishop) on Sep 07, 2004 at 18:50 UTC

    I don't think i have seen this one yet, although it is close in spirit to demerphq's solution:

    use strict; no warnings; my @array = reverse 'a' .. 'j'; my $var = shift() - 1; print join '', reverse @array[0..$var];

    Update: one-liner for the heck of it

    perl -le"@_=reverse a..j;print join'',reverse@_[0..shift()-1]" 5

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      perl -le "print((a..j)[-shift..-1])" 5
Re: fall through switch/case in perl
by radiantmatrix (Parson) on Sep 07, 2004 at 15:08 UTC
    Your example problem is fairly simple, but for more complicated switch-like behavior,
    my @switch_foo = ("a","b","c",...); for ($var) { my $junk = shift; while ($junk > 0) { print $switch_foo[$junk]); $junk--; } }
    It's way overkill for your particular problem, but for commplicated situations, @switch_foo can contain references to subs, making it quite powerful (if a little slow).

    In practice, however, if your algorithm needs a switch equivalent, you're better off re-thinking your algorithm.

    Update: fixed the code so that it works!

    --
    $me = rand($hacker{perl});
      Your code will either not print anything, or it'll never finish printing.

      In practice, however, if your algorithm needs a switch equivalent, you're better off re-thinking your algorithm.

      Eh? And why is that? That's a pretty bold statement, considering many languages do have a 'switch', perl6 will have a switch, there's Switch.pm, both the manual and the FAQ discuss it (without dismissing it as choicing a bad algorithm), and it has been on the wishlist since the very first release of Perl in 1987.

        Your code will either not print anything, or it'll never finish printing.

        You're right, I forgot the $junk--; after the print statement.

        That's a pretty bold statement, considering many languages do have a 'switch', perl6 will have a switch, there's Switch.pm, both the manual and the FAQ discuss it (without dismissing it as choicing a bad algorithm), and it has been on the wishlist since the very first release of Perl in 1987.

        I never said switch was bad code. Perl6 will have a switch because a lot of people want it, and it is very readable. However, switch statements are unbelievably over-used. I stand by my statement; chances are, if your algorithm needs switch, you can come up with a better algorithm. There are, of course, a few exceptions.

        As to your "considering many languages have a 'switch'", multiplicity does not correctness make.

        --
        $me = rand($hacker{perl});