Obtain a switch/case behaviour in Perl 5

40,294

Solution 1

If you are using Perl 5.10 you have given/when which is a switch statement (note, it can do more than compare with regexes, read the linked docs to see its full potential):

#or any of the dozen other ways to tell 5.10 to use its new features
use feature qw/switch/; 

given($string) {
    when (/^abc/) { $abc     = 1; }
    when (/^def/) { $def     = 1; }
    when (/^xyz/) { $xyz     = 1; }
    default       { $nothing = 1; }
}

If you are using Perl 5.8 or earlier you must make do with if/elsif/else statements:

if    ($string =~ /^abc/) { $abc     = 1; }
elsif ($string =~ /^def/) { $def     = 1; }
elsif ($string =~ /^zyz/) { $xyz     = 1; }
else                      { $nothing = 1; }

or nested condition operators (?:):

$string =~ /^abc/ ? $abc     = 1  :
$string =~ /^def/ ? $def     = 1  :
$string =~ /^xyz/ ? $xyz     = 1  :
                    $nothing = 1;

There is a module in Core Perl (Switch) that gives you fake switch statements via source filters, but it is my understanding that it is fragile:

use Switch;

switch ($string) {
    case /^abc/ {
    case /^abc/ { $abc     = 1 }
    case /^def/ { $def     = 1 }
    case /^xyz/ { $xyz     = 1 } 
    else        { $nothing = 1 }
}

or the alternate syntax

use Switch 'Perl6';

given ($string) {  
    when /^abc/ { $abc     = 1; }
    when /^def/ { $def     = 1; }
    when /^xyz/ { $xyz     = 1; }
    default     { $nothing = 1; }
}

Solution 2

The suggestion in Programming Perl is:


for ($string) {
    /abc/ and do {$abc    = 1; last;};
    /def/ and do {$def    = 1; last;};
    /xyz/ and do {$xyz    = 1; last;};
    $nothing = 1;
}

Solution 3

Just a short comment about the core Switch module that's been mentioned a couple of times in answers. The module in question relies on source filters. Among other things, that may result in wrong lines reported for errors. It's so bad that none of the core developers really remembers or cares to remember why it was accepted into the perl core in the first place.

Furthermore, Switch.pm will be the first Perl module ever to be removed from the perl core. The next major release of perl, 5.12.0, will still have it, albeit with a deprecation warning. That deprecation warning will go away if you explicitly install Switch.pm from CPAN. (You get what you ask for.) In the next release down the road, 5.14, Switch.pm will be entirely removed from core.

Solution 4

An equivalent solution that I like is a dispatch table.

my $switch = {
  'case1' => sub { print "case1"; },
  'case2' => sub { print "case2"; },
  'default' => sub { print "unrecognized"; }
};
$switch->{$case} ? $switch->{$case}->() : $switch->{'default'}->();
Share:
40,294
lurks
Author by

lurks

Software developer. Geek. Hipster? I live in Argentina. Programming languages I find interesting: C++, Python, Lisp, Javascript, Clojure, and anything that runs on top of the JVM (but not the Java language itself though). I feel more comfortable under Unix environments.

Updated on January 16, 2020

Comments

  • lurks
    lurks over 4 years

    Is there a neat way of making a case or switch statement in Perl 5?. It seems to me they should include a switch on version 6..

    I need this control structure in a script, and I've heard you can import a "switch module". But how can I achieve it without imports to minimize dependencies and acquire portability?