Home > other >  How to rewrite uses of given/when so to eliminate "experimental" warnings for their use
How to rewrite uses of given/when so to eliminate "experimental" warnings for their use

Time:07-07

ALL,

I have following:

This is perl 5, version 26, subversion 3 (v5.26.3) built for x86_64-linux-thread-multi
(with 57 registered patches, see perl -V for more detail)

Copyright 1987-2018, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

And the script I'm using have given and when.

Trying to Google I found this, which says it will be changed in an incompatible way.

I'm on RHEL8 and moving forward.

I'd like to fix the script, so that other get advantage of the script as well and don't see those warnings anymore.

How do I rewrite it to get rid of the warning?

TIA!

EDIT:

Sorry for not being explicit enough - by fixing the script I meant eliminating the problematic call/rewriting it.

EDIT2:

given($pl) {
  when(0) { print "A"; }
  when(1) { print "B"; }
  when(2) { print "C"; }
  when(3) { print "D"; }
  when(4) { print "E"; }
  default { print "Illegal "; }
}

where $pl is just an integer (unsigned short in C).

The script reads some data from the file. The file contains some hex values. Those values are parsed - some bits are extracted ` and then the string representation of those bits are printed on screen.

EDIT3:

I tried to use following code:

my %table_pl = (
  0 => sub { print "A"; },
  1 => sub { print "B"; },
  2 => sub { print "C"; },
  3 => sub { print "D"; },
  4 => sub { print "E"; },
);
for $pl( 0..5 )
{
    if( exists $table_pl{$pl} )
    {
        $table_pl{$pl}->();
    }
    else
    {
        print "Illegal ";
    }
}

but I got an error:

Number found where operator expected near 1
     (Missing semicolon on the previous line?)

CodePudding user response:

I like to use dispatch tables for things like this. Essentially it's a hash where the keys are the valid options, and the value of the key is a subroutine that does what you want to do when a valid option is supplied. Doing it this way eliminates a long chain of if/else statements.

You can put the subroutines inline in the table itself, or reference a subroutine defined elsewhere. The example here shows both:

use warnings;
use strict;

my %table = (
    0   => sub { print "A\n"; },
    1   => sub { print "B\n"; },
    2   => sub { print "C\n"; },
    3   => sub { print "D\n"; },
    4   => \&code_reference_example,
);

for my $pl (0..5) {
    if (exists $table{$pl}) {
        # Put the option into the table and execute
        # the assigned subroutine

        $table{$pl}->();

    } else {
        print "'$pl' is not a valid option\n";
    }
}

sub code_reference_example {
    print "E\n";
}

Output:

A
B
C
D
E
'5' is not a valid option

CodePudding user response:

given ( $pl ) {
  when ( 0 ) { print "A"; }
  when ( 1 ) { print "B"; }
  when ( 2 ) { print "C"; }
  when ( 3 ) { print "D"; }
  when ( 4 ) { print "E"; }
  default    { print "Illegal "; }
}

is short for

given ( $pl ) {
  when ( $_ ~~ 0 ) { print "A"; }
  when ( $_ ~~ 1 ) { print "B"; }
  when ( $_ ~~ 2 ) { print "C"; }
  when ( $_ ~~ 3 ) { print "D"; }
  when ( $_ ~~ 4 ) { print "E"; }
  default          { print "Illegal "; }
}

It can be written as

for ( $pl ) {
  if    ( $_ ~~ 0 ) { print "A"; }
  elsif ( $_ ~~ 1 ) { print "B"; }
  elsif ( $_ ~~ 2 ) { print "C"; }
  elsif ( $_ ~~ 3 ) { print "D"; }
  elsif ( $_ ~~ 4 ) { print "E"; }
  else              { print "Illegal "; }
}

But the whole point of the question is to avoid the broken/experimental smart matching feature. This would be the "equivalent":

for ( $pl ) {
  if ( !looks_like_number( $_ ) ) {
     print "Illegal ";
     next;
  }
  
  if    ( $_ == 0 ) { print "A"; }
  elsif ( $_ == 1 ) { print "B"; }
  elsif ( $_ == 2 ) { print "C"; }
  elsif ( $_ == 3 ) { print "D"; }
  elsif ( $_ == 4 ) { print "E"; }
  else              { print "Illegal "; }
}

It's not completely equivalent. The difference is that this last one treats the number 2 and the string 2 identically. The fact that smart matching treats them differently is the entire reason it's an unacceptable design, so this is desirable.

Finally, a dispatch table would work better here. A hash is often used, but an array could be used for the example given.

use Scalar::Util qw( looks_like_number );

sub is_nonneg_int {
   my $x = shift;
   return looks_like_number( $x ) && int( $x ) == $x && $x >= 0;
}

my @dispatch = (
  sub { print "A"; },
  sub { print "B"; },
  sub { print "C"; },
  sub { print "D"; },
  sub { print "E"; },
);

my $handler = is_nonneg_int( $pl ) ? $dispatch[ $pl ] : undef;
if ( $handler ) {
   $handler->();
} else {
   print "Illegal ";
}

The advantage of a dispatch table is that a single lookup is done instead of multiple comparisons.

  • Related