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

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

Growing up with an actuary as a Father meant lots of mathematics while growing up. OK, I had an aptitude for it, that helped. There were a lot of number and word games, and one of them was called fizz-buzz. You could play it anywhere, around the dinner table, while out for a walk in the Morgan Arboretum, or on a long car ride to or from Cape Cod. It goes like this:

The players recite the numbers in ascending order, starting at 1, except when you get to a multiple of 3, you say 'fizz', and when you get to a multiple of 5 you say 'buzz'. The number before 16, is, of course, fizz-buzz. To make things more interesting and a bit more challenging, my Father added 'sausage' when the number was a multiple of 7, thus the trifecta was fizz-buzz-sausage after 104 turns.

This morning I wanted to try out Perl's new switch statement ..

#!/usr/bin/perl510 use feature 'switch'; use feature 'say'; { for ( 1..40 ) { my @what; given ( $_ ) { when ( $_ % 3 == 0 ) { push ( @what, 'fizz' ); } when ( $_ % 5 == 0 ) { push ( @what, 'buzz' ); } when ( $_ % 7 == 0 ) { push ( @what, 'sausage' ); } } say join(' ',$_,@what); } }

but found it didn't produce the output I expected. Unlike C's switch statement, once a condition is met, the entire given construct is finished.

1 2 3 fizz 4 5 buzz 6 fizz 7 sasusage 8 9 fizz 10 buzz 11 12 fizz 13 14 sasusage 15 fizz 16 17 18 fizz 19 20 buzz 21 fizz 22 23 24 fizz 25 buzz 26 27 fizz 28 sasusage 29 30 fizz 31 32 33 fizz 34 35 buzz 36 fizz 37 38 39 fizz 40 buzz

That's OK -- now I know. Oh, and to get this to run, I had to run Perl 5.10 from the directory I built it in, and include -Ilib on the command line (thanks mauke). I made the mistake of upgrading the system Perl one time, and that's a mistake I won't ever make again.

Alex / talexb / Toronto

"Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

Replies are listed 'Best First'.
Re: Perl 5.10: switch statement demo
by Limbic~Region (Chancellor) on Dec 19, 2007 at 20:24 UTC
    talexb,
    Golf Challenge: FizzBuzz may be of interest to you. Also, take a look at perlsyn. While there is an implicit break in every when clause, you can get around this by using an explicit continue. A few other notes, you could use feature qw/say switch/;. There is more discussion of perl 5.10 doodads at Bring Out Your New Perl Code. For example:
    #!/usr/bin/perl use 5.010; use strict; use warnings; for (1 .. 105) { my $what = ''; given ($_) { when (not $_ % 3) { $what .= ' fizz'; continue } when (not $_ % 5) { $what .= ' buzz'; continue } when (not $_ % 7) { $what .= ' sausage' } } say "$_$what"; }

    Cheers - L~R

      I wonder why you switched from "when the remainder is zero" to "when not the remainder". (Reminds me of the baby in Dinosaurs: "Not the Momma! Not the Momma!")

      Update: Nevermind, I guess it can be read "when no remainder". I guess I don't think of a remainder of zero as there being no remainder, so it looks weird to me.

        The modulo construct can also be thought of as 'divisible by' -- so $foo % 3 checks to see if $foo is divisible by 3. Or at least that's the way my brain works.

        Alex / talexb / Toronto

        "Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

Re: Perl 5.10: switch statement demo
by jdporter (Chancellor) on Dec 19, 2007 at 20:26 UTC

    That doesn't look like a particularly apt illustration of given/when anyway, since it completely circumvents given's smart matching. It certainly doesn't look any better than

    $_ % 3 or push @what, 'fizz'; $_ % 5 or push @what, 'buzz'; $_ % 7 or push @what, 'sausage';
    which, as you pointed out, DWYM in this case, while given/when doesn't.

    A word spoken in Mind will reach its own level, in the objective world, by its own weight
Re: Perl 5.10: switch statement demo
by Thilosophy (Curate) on Dec 20, 2007 at 01:34 UTC
    but found it didn't produce the output I expected. Unlike C's switch statement, once a condition is met, the entire given construct is finished.

    It has been pointed out that you can explicitly continue to produce the output you expected.

    Please note, however, that the behaviour of continue is very much different from the fall-through in C, in that the next when condition is still being checked; in C, you go to the contents of the next case without any checks (which would not make much sense in a switch/case anyway).

    So the C behaviour (which is difficult to reproduce with the Perl given construct,see this thread) would not produce your expected output either.

Re: Perl 5.10: switch statement demo
by ikegami (Patriarch) on Dec 19, 2007 at 22:48 UTC
    Judging by how often people forget to place break;, it's much more natural for us to think of cases as exclusive. Or maybe it's because it's much more common to want exclusive cases, in which case it also saves us needless typing.
        Judging by how often people forget to place break;, it's much more natural for us to think of cases as exclusive.

      I dunno -- remembering to add the break; is just one of the things that I know and will never forget when I write C. You can call it a feature or a bug, but it's part of the language that I know.

      I didn't write the node to complain, really, it's more of a warning for anyone else who grew up writing C. And it's spawned a couple of really good responses detailing alternative approaches that work, both before and after 5.10. And that discussion, by itself, is very useful.

      Alex / talexb / Toronto

      "Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

Re: Perl 5.10: switch statement demo
by LighthouseJ (Sexton) on Dec 20, 2007 at 13:42 UTC
    I don't know why people want a verbatim 'switch' statement when you can use a for-statement.
    #!/usr/bin/perl -w use strict; { for (1..40) { print; ($_ % 3 == 0) && print q! fizz!; ($_ % 5 == 0) && print q! buzz!; ($_ % 7 == 0) && print q! sausage!; print "\n"; } }

    I generally use a for loop in conjunction with regular expression matches, like this trivial example:
    #!/usr/bin/perl -w use strict; { my @items = qw/The three principal virtues of a programmer are Laz +iness, Impatience, and Hubris. See the Camel Book for why/; for (@items) { /^[A-Z]/ && do { print "Found a proper noun? $_\n"; next; }; /[,.]/ && do { print "This word has punctuation: $_\n"; next; }; print "This word seems uninteresting: $_\n"; } }

    It has a lot of flexibility where you can mix and match parts and get real nice uses out of it. I've always used it fully and got very good results --without waiting for a proper switch statement.

    "The three principal virtues of a programmer are Laziness, Impatience, and Hubris. See the Camel Book for why." -- `man perl`
      You can use when in a for loop:
      for (@items) { when (/^[A-Z]/) { say "Found a proper noun? $_"; } when (/[,.]/) { say "This word has punctuation: $_"; } say "This word seems uninteresting: $_"; }
        I guess that's kind of my point. The really useful and powerful things in todays computing industry like Perl, UNIX, even RISC processors all merely provide you with a basic set of tools and rely on you (the user) to put the simple tools together to make a useful mechanism.

        Stuff like adding a named "switch" statement or needing a "when" keyword just complicates it unnecessarily IMO.

        I had a discussion about this with an Oracle DBA. Needless to say we maintained our differences of opinion.

        "The three principal virtues of a programmer are Laziness, Impatience, and Hubris. See the Camel Book for why." -- `man perl`