Pretty soon after I start­ed writ­ing Perl in 1994, I noticed that it lacked a con­struct often found in oth­er lan­guages: the switch state­ment. Not to wor­ry, though—you can achieve the same effect with a cas­cad­ing series of if-elsif state­ments, right?

Well, no, you should­n’t do that, espe­cial­ly if the chain is real­ly long. There’s even a perl­crit­ic pol­i­cy about it, which sug­gests that you use given and when instead.

But given and when (and the smart­match oper­a­tor they imply, ~~) are still con­sid­ered exper­i­men­tal, with behav­ior sub­ject to change. So what’s a respon­si­ble devel­op­er to do?

The answer is to use the for state­ment as a top­i­cal­iz­er, which is a fan­cy way of say­ing it assigns its expres­sion to $_. You can then use things that act on $_ to your heart’s con­tent, like reg­u­lar expres­sions. Here’s an example:

for ($foo) {
    /^abc/ and do {
        ...
        last;
    };
    /^def/ and do {
        ...
        last;
    };
    # FALL THRU
    ...
}

This will cov­er a lot of cas­es (haha, see what I did there? A lot of lan­guages use a case state­ment… oh, nev­er mind.) And if all you’re doing is exact string match­ing, there’s no need to bring in reg­ex­ps. You can use a hash as a lookup table:

my %lookup = (
    foo => sub { ... },
    bar => sub { ... },
);
$lookup{$match}->();

EDIT: If every alter­na­tive is assign­ing to the same vari­able, a ternary table is anoth­er pos­si­bil­i­ty. This is a chained set of ternary con­di­tion­al (? :) oper­a­tors arranged for read­abil­i­ty. I first heard about this tech­nique from Damian Conway’s Perl Best Practices (2005).

           # Name format                 # Salutation
my $salute = $name eq ''                 ? 'Dear Customer'
           : $name =~ /(Mrs?[.]?\s+\S+)/ ? "Dear $1"
           : $name =~ /(.*),\s+Ph[.]?D/  ? "Dear Dr. $1"
           :                               "Dear $name"
           ;

Note that although this is just as inef­fi­cient as a cascaded-if/​elsif, it’s more clear that it’s a sin­gle assign­ment. It’s also more com­pact, and reads like a table with columns of match­es and alternatives.

Any of these pat­terns are prefer­able to cas­cad­ing if/​elsifs. And if you want to mon­i­tor the devel­op­ment of given, when, and ~~, check this issue on GitHub. It was last com­ment­ed on eight years ago, though, so I would­n’t hold my breath.

6 thoughts on “Switching up my switches in Perl

  1. I’ve found this to be no more com­pli­cat­ed than a switch in oth­er lan­guages, but just as lim­it­ed. You can’t, for exam­ple, write >32. You must use $_>32 and that point you might as well be using if else.

    • That’s fair. But the vast major­i­ty of the time, you’re either match­ing against a fixed set of strings (use a hash lookup), a fixed set of reg­u­lar expres­sions (use the top­i­cal­ized for I men­tion), or you’re set­ting the same vari­able based on a deci­sion tree (use a tab­u­lar ternary as Conway cov­ers in Perl Best Practices).

      • Which I agree with. Which leads me to believe that the smart match tech­nol­o­gy that was a big part of giv­en when was an over­reach and the main rea­son that given/​when is still exper­i­men­tal. Would have been nice I’ve giv­en when was just a switch and then smart match could have been added later.

  2. I know that given/​when and smart­match have already been dep­re­cat­ed some years ago and I think they have been removed from the lan­guage now.

Comments are closed.