[ABE.pm] My first enclosure doesn't work. :-(

Walt Mankowski waltman at pobox.com
Fri Oct 26 12:46:55 PDT 2007


On Fri, Oct 26, 2007 at 01:30:50PM -0400, Faber J. Fedor wrote:
> I'm porting a script to a webapp.  One of the changes I want to do is to
> dynamically generate an if-elsif.  After thinking it through, it seems
> like an enclosure would do what I want but I can't get it to work. :-( 
> 
> I want to dynamically generate this from a hash:
> 
>   if ($capt < 100)
>     {return (3.0);}
>   elsif (($capt >= 100) && ($capt < 250))
>     {return (1.5);}
>   ...
>   elsif (($capt >= 10000) && ($capt < 50000))
>     {return (0.01);}
>   elsif ($capt >= 50000)
>     {return (0.005);}
> 
> In my enclosure, I generate my $stmt much as I would build an SQL
> statement (yes, I know about DBIC; now get off my lawn!) so that, at the
> end of the enclosure, if I 'print $stmt' I see a legit if-elsif
> statement.
> 
> Now what? At the end of my enclosure, I say 'return sub { $stmt };' or
> 'return sub { eval($stmt) };'. If I then do a 'print
> make_if_elsif(300)' I get 'CODE(0x8096228)' instead of the expected
> '1.0'.
> 
> What am I doing wrong?
> 
> You can find my script at http://www.faberfedor.com/enclosure_example.pl

What are you doing wrong?  Well, for starters, whenever you see a long
string of if/elses like that, you should try to see if you can replace
it with a loop, or something that's table-driven.  Trying to
auto-generate the code for the if/elses is just making an already bad
situation even more complex than it needs to be.

The original code is also more complex than it needs to be because
you're checking the lower and upper bound for each range.  Since there
aren't any holes in the ranges, you only need to check the upper
range.

I also don't understand why you want to use a hash when it seems
simpler to me to use an array.

I don't know what an "enclosure" is, but this seems a much simpler
solution to your problem:

#!/usr/bin/perl -wl
use strict;

print costCalc($ARGV[0]);

sub costCalc {
    my ($capt) = @_;
    my @costCalcArr = ( [50000  => 0.005],
                        [10000  => 0.01],
                        [7500   => 0.025],
                        [5000   => 0.05],
                        [2500   => 0.15],
                        [1000   => 0.25],
                        [750    => 0.5],
                        [500    => 0.75],
                        [250    => 1.0],
                        [100    => 1.5],
                      );

    return 0.0 if $capt eq "";

    for my $ar (@costCalcArr) {
        return $ar->[1] if $capt >= $ar->[0];
    }

    return 3.0;
}

I suppose you could try doing a binary search on the interval cutoffs,
but with only 10 values it's probably not worth the trouble.  You
could also store the intervals in a binary tree or a range tree, but
again, it's probably not worth the trouble.

Walt


More information about the ABE-pm mailing list