简体   繁体   中英

Pattern matching in Perl ala Haskell

In Haskell (F#, Ocaml, and others), I can do this:

sign x |  x >  0        =   1
       |  x == 0        =   0
       |  x <  0        =  -1

Which calculates the sign of a given integer.

This can concisely express certain logic flows; I've encountered one of these flows in Perl.

Right now what I am doing is

sub frobnicator
{
   my $frob = shift;
   return "foo" if $frob eq "Foomaticator";
   return "bar" if $frob eq "Barmaticator";
   croak("Unable to frob legit value: $frob received");
}

Which feels inexpressive and ugly.

This code has to run on Perl 5.8.8, but of course I am interested in more modern techniques as well.

Here's one other possible approach, which is still a bit wordy, but perhaps reflects the flow better. (In the book Perl Best Practices, the author suggests mostly avoiding the postfix form of if because you need to scan all the way to the right to understand what is going on.) You could add parentheses if that helps to highlight the condition.

sub sign
{
    my $x = shift;
    $x  > 0  and return  1;
    $x == 0  and return  0;
    $x  < 0  and return -1;
}

sub frobnicator
{
    my $frob = shift;

    $frob eq "Foomaticator" and return "foo";
    $frob eq "Barmaticator" and return "bar";

    croak("Unable to frob legit value: $frob received");
}

You could use the conditional operator:

sub frobnicator {
  my $frob = shift;
  return $frob eq 'Foomaticator' ? 'foo' :
         $frob eq 'Barmaticator' ? 'bar' :
         croak("Unable to frob legit value: $frob received");
}

It turns your original example into something like:

sub sign {
  my $x = shift;
  return $x < 0 ? -1 :
         $x > 0 ?  1 :
                   0 ;
}

Which, y'know, is a little ugly, but it gets the point across :-)

sub sign { 
    my $x = shift;
    return $x <=> 0;
}

Expressive? If you know what the "spaceship" operator does, yes. If not, not so much. How expressive a piece of perl code feels to you is entirely dependent on your familiarity with perl, I would say.

I must say I've no idea what you are after. To me, the code in your example is perfectly clear and not in the least ugly. There's always alternatives in perl, so how about:

sub frobnicator
{
   my $frob = shift;
   my %frobs = (
       Foomaticator    => "foo",
       Barmaticator    => "bar",
   );
   return $frobs{$frob} //  # If $frob is a defined key, return it
       croak("Unable to frob legit value: $frob received");
}

Slightly less precise, but perhaps more friendly to older perl versions is using || instead of // .

For what it's worth, Perl 5.10 or later gives you:

given

...which would allow:

...
use feature qw( switch say);
use Carp;
sub frobnicator {
    my $frob = shift;
    given ($frob) {
        when ('Foomaticator') {
            return q(foo);
        }
        when ('Barmaticator') {
            return q(bar);
        }
        default {
            croak(qq(Unable to frob legit value: $frob received));
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM