diff options
author | Robin Houston <robin@cpan.org> | 2005-12-17 20:44:31 +0000 |
---|---|---|
committer | Rafael Garcia-Suarez <rgarciasuarez@gmail.com> | 2005-12-19 16:26:15 +0000 |
commit | 0d863452f5cac86322a90184dc68dbf446006ed7 (patch) | |
tree | a6b225c0f732e2062a2c430a359c1c1db88fa36c /pod/perlsyn.pod | |
parent | 4f5010f268a8de0d9ea78da367041150ef2777f4 (diff) | |
download | perl-0d863452f5cac86322a90184dc68dbf446006ed7.tar.gz |
latest switch/say/~~
Message-Id: <20051217204431.GB28940@rpc142.cs.man.ac.uk>
p4raw-id: //depot/perl@26400
Diffstat (limited to 'pod/perlsyn.pod')
-rw-r--r-- | pod/perlsyn.pod | 231 |
1 files changed, 122 insertions, 109 deletions
diff --git a/pod/perlsyn.pod b/pod/perlsyn.pod index c819b94a8d..cc91e31521 100644 --- a/pod/perlsyn.pod +++ b/pod/perlsyn.pod @@ -466,8 +466,8 @@ rather than merely terminating the inner one. And it's faster because Perl executes a C<foreach> statement more rapidly than it would the equivalent C<for> loop. -=head2 Basic BLOCKs and Switch Statements -X<switch> X<block> X<case> +=head2 Basic BLOCKs +X<block> A BLOCK by itself (labeled or not) is semantically equivalent to a loop that executes once. Thus you can use any of the loop control @@ -476,7 +476,7 @@ I<NOT> true in C<eval{}>, C<sub{}>, or contrary to popular belief C<do{}> blocks, which do I<NOT> count as loops.) The C<continue> block is optional. -The BLOCK construct is particularly nice for doing case +The BLOCK construct can be used to emulate case structures. SWITCH: { @@ -486,133 +486,146 @@ structures. $nothing = 1; } -There is no official C<switch> statement in Perl, because there are -already several ways to write the equivalent. +Such constructs are quite frequently used, because older versions +of Perl had no official C<switch> statement. -However, starting from Perl 5.8 to get switch and case one can use -the Switch extension and say: - use Switch; +=head2 Switch statements +X<switch> X<case> X<given> X<when> X<default> -after which one has switch and case. It is not as fast as it could be -because it's not really part of the language (it's done using source -filters) but it is available, and it's very flexible. +Starting from Perl 5.10, you can say -In addition to the above BLOCK construct, you could write + use feature "switch"; - SWITCH: { - $abc = 1, last SWITCH if /^abc/; - $def = 1, last SWITCH if /^def/; - $xyz = 1, last SWITCH if /^xyz/; - $nothing = 1; +which enables a switch feature that is closely based on the +Perl 6 proposal. + +The keywords C<given> and C<when> are analogous +to C<switch> and C<case> in other languages, so the code +above could be written as + + given($_) { + when (/^abc/) { $abc = 1; } + when (/^def/) { $def = 1; } + when (/^xyz/) { $xyz = 1; } + default { $nothing = 1; } } -(That's actually not as strange as it looks once you realize that you can -use loop control "operators" within an expression. That's just the binary -comma operator in scalar context. See L<perlop/"Comma Operator">.) +This construct is very flexible and powerful. For example: -or + given() { - SWITCH: { - /^abc/ && do { $abc = 1; last SWITCH; }; - /^def/ && do { $def = 1; last SWITCH; }; - /^xyz/ && do { $xyz = 1; last SWITCH; }; - $nothing = 1; + xxxx } -or formatted so it stands out more as a "proper" C<switch> statement: +Most of its power comes from the implicit smart matching: - SWITCH: { - /^abc/ && do { - $abc = 1; - last SWITCH; - }; - - /^def/ && do { - $def = 1; - last SWITCH; - }; - - /^xyz/ && do { - $xyz = 1; - last SWITCH; - }; - $nothing = 1; - } + when($foo) ... -or +is exactly equivalent to - SWITCH: { - /^abc/ and $abc = 1, last SWITCH; - /^def/ and $def = 1, last SWITCH; - /^xyz/ and $xyz = 1, last SWITCH; - $nothing = 1; - } + when($_ ~~ $foo) ... -or even, horrors, - - if (/^abc/) - { $abc = 1 } - elsif (/^def/) - { $def = 1 } - elsif (/^xyz/) - { $xyz = 1 } - else - { $nothing = 1 } - -A common idiom for a C<switch> statement is to use C<foreach>'s aliasing to make -a temporary assignment to C<$_> for convenient matching: - - SWITCH: for ($where) { - /In Card Names/ && do { push @flags, '-e'; last; }; - /Anywhere/ && do { push @flags, '-h'; last; }; - /In Rulings/ && do { last; }; - die "unknown value for form variable where: `$where'"; - } +(though you need to enable the "~~" feature before you +can use the C<~~> operator directly). In fact C<when(EXPR)> +is treated as an implicit smart match most of the time. The +exceptions are that when EXPR is: + +=over 4 + +=item o + +a subroutine or method call + +=item o + +a regular expression match, i.e. C</REGEX/> or C<$foo =~ /REGEX/>, +or a negated regular expression match C<$foo !~ /REGEX/>. + +=item o + +a comparison (such as C<$_ E<lt> 10> or C<$x gt "abc"> + +=item o + +C<defined(...)>, C<exists(...)>, or C<eof(...)> + +=item o -Another interesting approach to a switch statement is arrange -for a C<do> block to return the proper value: +A negated expression C<!(...)> or C<not (...)>, or a logical +exclusive-or C<(...) xor (...)>. - $amode = do { - if ($flag & O_RDONLY) { "r" } # XXX: isn't this 0? - elsif ($flag & O_WRONLY) { ($flag & O_APPEND) ? "a" : "w" } - elsif ($flag & O_RDWR) { - if ($flag & O_CREAT) { "w+" } - else { ($flag & O_APPEND) ? "a+" : "r+" } +=back + +then the value of EXPR is used directly as a boolean. +Furthermore: + +=over 4 + +=item o + +If EXPR is C<... && ...> or C<... and ...>, the test +is applied recursively to both arguments. If I<both> +arguments pass the test, then the argument is treated +as boolean. + +=item o + +If EXPR is C<... || ...> or C<... or ...>, the test +is applied recursively to the first argument. + +=back + +These rules look complicated, but usually they will do what +you want. For example you could write: + + when (/^\d$/ && $_ < 75) { ... } + +C<default> behaves exactly like C<when(1 == 1)>, which is +to say that it always matches. + +See L</"Smart matching in detail"> for more information +on smart matching. + +=head3 Fall-through + +You can use the C<continue> keyword to fall through from one +case to the next: + + given($foo) { + when (/x/) { print "\$foo contains an 'x'\n"; continue } + when (/y/) { print "\$foo contains a 'y'\n" } + default { print "\$foo contains neither an 'x' nor a 'y' } } - }; - -Or - - print do { - ($flags & O_WRONLY) ? "write-only" : - ($flags & O_RDWR) ? "read-write" : - "read-only"; - }; - -Or if you are certain that all the C<&&> clauses are true, you can use -something like this, which "switches" on the value of the -C<HTTP_USER_AGENT> environment variable. - - #!/usr/bin/perl - # pick out jargon file page based on browser - $dir = 'http://www.wins.uva.nl/~mes/jargon'; - for ($ENV{HTTP_USER_AGENT}) { - $page = /Mac/ && 'm/Macintrash.html' - || /Win(dows )?NT/ && 'e/evilandrude.html' - || /Win|MSIE|WebTV/ && 'm/MicroslothWindows.html' - || /Linux/ && 'l/Linux.html' - || /HP-UX/ && 'h/HP-SUX.html' - || /SunOS/ && 's/ScumOS.html' - || 'a/AppendixB.html'; + +=head3 Switching in a loop + +Instead of using C<given()>, you can use a C<foreach()> loop. +For example, here's one way to count how many times a particular +string occurs in an array: + + my $count = 0; + for (@array) { + when ("foo") { ++$count } } - print "Location: $dir/$page\015\012\015\012"; + print "\@array contains $count copies of 'foo'\n"; + +On exit from the C<when> block, there is an implicit C<next>. +You can override that with an explicit C<last> if you're only +interested in the first match. + +This doesn't work if you explicitly specify a loop variable, +as in C<for $item (@array)>. You have to use the default +variable C<$_>. (You can use C<for my $_ (@array)>.) + +=head3 Smart matching in detail + + -That kind of switch statement only works when you know the C<&&> clauses -will be true. If you don't, the previous C<?:> example should be used. +=head3 Custom matching via overloading -You might also consider writing a hash of subroutine references -instead of synthesizing a C<switch> statement. +You can change the way that an object is matched by overloading +the C<'~~'> operator. This trumps the usual smart match semantics. =head2 Goto X<goto> |