Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

falling through cases?

by John M. Dlugosz (Monsignor)
on Feb 27, 2003 at 22:55 UTC ( #239280=perlquestion: print w/replies, xml ) Need Help??
John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

In C, it's easy to write a switch/case statement that falls through, to implement Duff's Device or other cases where to handle the last partial frame of data you want to jump into a sequence of instructions as some point. That is, if my n is 8, do 8 statements; if n is 7, skip the first; if n is 6, skip the first two, etc.

How would you efficiently do that in Perl 5.8? The obvious way evaluates all the conditions even though we know that once one is true the rest will be true also.


Replies are listed 'Best First'.
Re: falling through cases?
by tachyon (Chancellor) on Feb 27, 2003 at 23:05 UTC

    There are several really good solutions at Eek! goto? This was when the accidental duplicate nodes stuff was happening so you probably want to look at both nodes.

    BrowserUK shows a really elegant solution using goto, I posted a hash dispatch method, Abigail-II offers discussion..... Switch even gets a mention but this uses 500 lines and goto internally anyway.




      Very interesting, especially since BrowserUk's node is the exact same code that I'm looking at! GMTA and all that.


Re: falling through cases?
by BrowserUk (Pope) on Feb 28, 2003 at 00:47 UTC

    This was my final version of the algorithm. It benchmarked quickest of all the versions (7) that I tried, but I haven't found a simple way of verifying that it's a faithful re-implementation of the C version.

    use constant A => 0; use constant B => 1; use constant C => 2; sub mix ($$$) { use integer; $_[A] -= $_[B]; $_[A] -= $_[C]; $_[A] ^= ($_[C]>>13); $_[B] -= $_[C]; $_[B] -= $_[A]; $_[B] ^= ($_[A]<< 8); $_[C] -= $_[A]; $_[C] -= $_[B]; $_[C] ^= ($_[B]>>13); $_[A] -= $_[B]; $_[A] -= $_[C]; $_[A] ^= ($_[C]>>12); $_[B] -= $_[C]; $_[B] -= $_[A]; $_[B] ^= ($_[A]<<16); $_[C] -= $_[A]; $_[C] -= $_[B]; $_[C] ^= ($_[B]>> 5); $_[A] -= $_[B]; $_[A] -= $_[C]; $_[A] ^= ($_[C]>> 3); $_[B] -= $_[C]; $_[B] -= $_[A]; $_[B] ^= ($_[A]<<10); $_[C] -= $_[A]; $_[C] -= $_[B]; $_[C] ^= ($_[B]>>15); } use constant GOLDEN_RATIO => 0x9e3779b9; use constant INITHASH => 2; sub hash { use integer; my($a,$b,$c) = (GOLDEN_RATIO, GOLDEN_RATIO, $_[INITHASH]); my ($p, $len) = (0, length $_[KEY]); do { my($x,$y,$z) = unpack 'V3', substr($_[KEY] . (chr(0)x11), $p, +12); mix($a += $x, $b += $y, $c += $z + $len); $p+=12; } while $p <= $len; return $c; }

    Examine what is said, not who speaks.
    1) When a distinguished but elderly scientist states that something is possible, he is almost certainly right. When he states that something is impossible, he is very probably wrong.
    2) The only way of discovering the limits of the possible is to venture a little way past them into the impossible
    3) Any sufficiently advanced technology is indistinguishable from magic.
    Arthur C. Clarke.
      Hm? Unless I'm missing something, that leaves out the part that's relevant to this thread of how to best code the case fall-through thing in Perl.

      If the input is not a multiple of 12 bytes, it needs to do this (from the original C version):

      switch(len) /* all the case statements fall through */ { case 11: c+=((ub4)k[10]<<24); case 10: c+=((ub4)k[9]<<16); case 9 : c+=((ub4)k[8]<<8); /* the first byte of c is reserved for the length */ case 8 : b+=((ub4)k[7]<<24); case 7 : b+=((ub4)k[6]<<16); case 6 : b+=((ub4)k[5]<<8); case 5 : b+=k[4]; case 4 : a+=((ub4)k[3]<<24); case 3 : a+=((ub4)k[2]<<16); case 2 : a+=((ub4)k[1]<<8); case 1 : a+=k[0]; /* case 0: nothing left to add */ } mix(a,b,c);
      And you left out the $c += $length (using 32-bit integer overflow).

      Hmm again... or is the . (chr(0)x11), $p, 12 taking the place of that, to fill out the last "gulp"?

      So even if that's not quite right, your answer to how to write such a switch statement is "don't. a more natural way (at a higher level in the algorithm) in Perl suggests itself and prevents the need".

      Back to the other thread: thanks for sharing fastest implementation of the mix() function you found in pure Perl.

Re: falling through cases?
by Abigail-II (Bishop) on Feb 28, 2003 at 00:16 UTC
    You mentione extreme cases (Duff's Device, which is more about jumping into a looping construct), and some lesser used ones, but you fail to mention the prime reason there's a fall through with cases in C:
    case FOO: case BAR: /* code here */ break;

    That is, having more values map to the same code.

    How to do that efficiently in Perl? It depends. Sometimes a computed goto is the best way, sometimes a series of if statements. Both have the disadvantage they potentially spend a lot of time finding the code that needs to be executed. And depending on the values of the cases, and in which order they are, you could use an array of code references, and execute each code reference from the switched value to the end.


Re: falling through cases?
by shotgunefx (Parson) on Feb 28, 2003 at 05:26 UTC
    It feels like a kludge but couldn't you do it with an array of code references if you wanted to avoid goto? (Though I don't care to ignore goto when it's appropriate)


    "To be civilized is to deny one's nature."

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://239280]
Approved by Mr. Muskrat
Front-paged by mojotoad
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2018-07-22 15:56 GMT
Find Nodes?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?

    Results (454 votes). Check out past polls.