in reply to alternatives to if and series of elsif

Something along these lines maybe, since the check is always identical ...
sub compare { my ($points, $quota) = @_; my @comparisons = ( # points, quota [ 18000, 24 ], [ 16000, 23 ], [ 14000, 22 ], ... ); foreach my $cmp ( @comparisons ){ return $cmp->[1] if $points > $cmp->[0] && $quota < $cmp->[1]; } return 15; }
Ah, now i see that there's a pattern... youre compare2 is close.. i think if you make it $points -= 2000; it will work.

Update: since patterned, can generate @comparisons like this:
my @comparisons = reverse map { [ ($_-15)*2000, $_ ] } 16 .. 24;
Update: fixed typo of $cmp[0] needing to be $cmp->[0]
Update: thanks to ihb for pointing out the need for the reverse of 16 .. 24 instead of just 24 .. 16 which is an empty list.

Replies are listed 'Best First'.
Re^2: alternatives to if and series of elsif
by kiat (Vicar) on Jul 02, 2005 at 02:18 UTC
    Thanks, davidrw!

    Like your foreach method.

    Btw, if the sub is called a lot, would it be faster to have the values hard-coded into @comparisons rather than have them generated using map?

    Added: I think you mistyped (i.e. missing ->):

    if $points > $cmp[0] && $quota < $cmp[1] Should be: if $points > $cmp->[0] && $quota < $cmp->[1]
      FWIW, performing a quick benchmark indicates that using a hard-coded @comparisons (davidrw's initial suggestion) is significantly faster:
      #!/usr/bin/perl -w use strict; use Benchmark qw(cmpthese); my ($points, $quota) = (1000, 20); cmpthese( -1, { 'op_orig' => sub { return op_orig($points, $quota);}, 'op_cmp2' => sub { return op_cmp2($points, $quota);}, 'davidrw_1' => sub { return davidrw_1($points, $quota);}, 'davidrw_2' => sub { return davidrw_2($points, $quota);}, 'ternary' => sub { return ternary_cmp($points, $quota);}, } ); sub op_orig { # OP, original compare my ($points, $quota) = @_; if ($points > 18000 && $quota < 24) { return 24; } elsif ($points > 16000 && $quota < 23) { return 23; } elsif ($points > 14000 && $quota < 22) { return 22; } elsif ($points > 12000 && $quota < 21) { return 21; } elsif ($points > 10000 && $quota < 20) { return 20; } elsif ($points > 8000 && $quota < 19) { return 19; } elsif ($points > 6000 && $quota < 18) { return 18; } elsif ($points > 4000 && $quota < 17) { #19 return 17; } elsif ($points > 2000 && $quota < 16) { #17 return 16; } return 15; } sub op_cmp2 { # OP, compare2 my ($points, $quota) = @_; my $base_quota = 15; while($points > 2000 && $quota > $base_quota) { $points -= 2000; $base_quota += 1; } return $base_quota; } sub davidrw_1 { # davidrw suggestion 1 my ($points, $quota) = @_; my @comparisons = ( # points, quota [18000, 24], [16000, 23], [14000, 22], [12000, 21], [10000, 20], [8000, 19], [6000, 18], [4000, 17], [2000, 16], ); foreach my $cmp (@comparisons) { return $cmp->[1] if $points > $cmp->[0] && $quota < $cmp->[1]; } return 15; } sub davidrw_2 { # davidrw suggestion 2 my ($points, $quota) = @_; my @comparisons = map { [($_ - 15) * 2000, $_] } 24 .. 16; foreach my $cmp (@comparisons) { return $cmp->[1] if $points > $cmp->[0] && $quota < $cmp->[1]; } return 15; } sub ternary_cmp { # Just for the fun of it my ($points, $quota) = @_; return ($points > 18000 && $quota < 24) ? 24 : ($points > 16000 && $quota < 23) ? 23 : ($points > 14000 && $quota < 22) ? 22 : ($points > 12000 && $quota < 21) ? 21 : ($points > 10000 && $quota < 20) ? 20 : ($points > 8000 && $quota < 19) ? 19 : ($points > 6000 && $quota < 18) ? 18 : ($points > 4000 && $quota < 17) ? 17 : ($points > 2000 && $quota < 16) ? 16 : 15; } __END__
                    Rate davidrw_1   op_orig   ternary davidrw_2   op_cmp2
      davidrw_1  22411/s        --      -86%      -86%      -87%      -90%
      op_orig   162293/s      624%        --       -1%       -4%      -29%
      ternary   163840/s      631%        1%        --       -3%      -28%
      davidrw_2 169239/s      655%        4%        3%        --      -26%
      op_cmp2   227555/s      915%       40%       39%       34%        --
      

      Update: Added kutsu's suggestion to the mix:

                    Rate     kutsu davidrw_1   op_orig   ternary davidrw_2   op_cmp2
      kutsu      15170/s        --      -31%      -90%      -91%      -91%      -93%
      davidrw_1  21976/s       45%        --      -86%      -86%      -87%      -90%
      op_orig   159288/s      950%      625%        --       -1%       -9%      -31%
      ternary   160777/s      960%      632%        1%        --       -8%      -30%
      davidrw_2 174121/s     1048%      692%        9%        8%        --      -24%
      op_cmp2   229681/s     1414%      945%       44%       43%       32%        --
      
        Very nice, thanks!

        Me need to go figure what what 15170/s means...

        I ran your benchmark code above, changing the points from 1000 to 10 000. I got the following results:

        Rate davidrw_2 davidrw_1 op_cmp2 op_orig ternary davidrw_2 48968/s -- -21% -87% -91% -91% davidrw_1 62049/s 27% -- -83% -89% -89% op_cmp2 363917/s 643% 487% -- -35% -36% op_orig 559112/s 1042% 801% 54% -- -2% ternary 570934/s 1066% 820% 57% 2% --
        Note that I changed the map code to:
        my @comparisons = map { [($_ - 15) * 2000, $_] } 16 .. 24;
        That is, 16 .. 24 instead of 24 .. 16.
Re^2: alternatives to if and series of elsif
by ihb (Deacon) on Jul 03, 2005 at 13:56 UTC

    Note that 24 .. 16 is an empty list. You probably want reverse map { ... } 16 .. 24.

    ihb

    See perltoc if you don't know which perldoc to read!