rhymejerky has asked for the wisdom of the Perl Monks concerning the following question:

Hi, If I have
$a = 3; $b = 4; $op = 'le';
Is it possible to do $r = eval("$a $op $b") where $r = 1 because 3 is lte 4? Reason I want to do that is because I have I have some operators pass in, in the form of 'le', 'gt', 'eq'. Currently, I do a if statement for each operator like
if ($op eq 'le') { if ($a <= $b) { do something } else { do something else } } elsif ($op eq 'ge') if ($a >= $b) { do something } else { do something else } } elsif ....
Just want to see if there is a cleaver way to consolidate all those if-else statement into 1 TIA

Replies are listed 'Best First'.
Re: eval question (avoid string eval References)
by eyepopslikeamosquito (Archbishop) on Jan 06, 2011 at 20:17 UTC
Re: eval question
by jwkrahn (Abbot) on Jan 06, 2011 at 20:08 UTC
    my %op_table = ( le => sub { $_[0] <= $_[1] }, ge => sub { $_[0] >= $_[1] }, # etc. ); exists $op_table{ $op } or die "The operator '$op' does not exist.\n"; if ( $op_table{ $op }->( $a, $b ) ) { do something } else { do something else }
      I ended up implementing it using your hash. I was thinking about doing eval and string comparison, but this would not work for cases like comparing 5 le 14. Also, not using eval save me from firing another process

        String eval doesn't launch another process. It merely runs the compiler again.

Re: eval question
by ikegami (Patriarch) on Jan 06, 2011 at 20:22 UTC

    Bah, if you have to use a hash anyway, eval doesn't help any.

    my %perlop = ( lte => sub { $_[0] <= $_[1] }, gte => sub { $_[0] >= $_[1] }, # ... ); my $r = $perlop{$op}->($a, $b);

    (Oops, seems I had the page opened a while without realising it, and others have already answered this since then.)

Re: eval question
by Anonyrnous Monk (Hermit) on Jan 06, 2011 at 19:49 UTC
    Is it possible to do $r = eval("$a $op $b")

    In principle yes, if you map the operators 'le', 'ge' etc. to the corresponding Perl equivalents — e.g. using a hash.

    my %perlop = ( le => '<=', ge => '>=', #... ); my $r = eval "$a $perlop{$op} $b";

    (But as always with string eval, be careful what you interpolate when the data ($a, $b here) doesn't come from trusted sources...)

      (But as always with string eval, be careful what you interpolate when the data doesn't come from trusted sources...)

      If you whitelist allowed operators via hash, and don't interpolate $a and $b, you should be fine:

      if (exists $perlop{$op}) { my $r = eval "\$a $perlop{$op} \$b"; } else { die "OH NOEZ!"; }

      That way the string passed to eval contains the variable names, and obtains their value from the outer scope.

Re: eval question
by locked_user sundialsvc4 (Abbot) on Jan 07, 2011 at 01:53 UTC

    I just don’t like “clever solutions” anymore.   The strategy that always seems to work best, at least for yours truly, is to find the simplest and most-obvious way to write code that expresses what you truly mean to say.   Then, let the mighty Perl interpreter do the rest.   After all, microprocessors are never going to slow down anytime soon.   They’ll only become faster.   But software maintenance and development costs will only increase.   And it will be the “clever” solutions, i.e. “the ones that surely must have mightily impressed the programmer himself,” that will bite you in the a*s first.   (Usually at three o’clock in the morning.)

      I guess I was looking for a cleaner solution than a more clever solution. With the hash, I was able to cut out 100 lines of repeated code and I can easily add addition operation using the hash. But I agree with you, I rather read longer code that is self explanatory than a bunch of clever hacks string together