What you are trying to do is to evaluate '$degree>=5' as real code. Rather than trying to evaluate the string as code (which can be done with eval), it's usually safer and often more robust to instead pass a code-reference. You can use a generator subroutine to generate conditional subs on demand, like this:
sub generate_condition {
my ( $test, $bound ) = @_;
return sub { return $test >= $bound; };
}
my %condition;
$condition{'hub'}{'1'} = generate_condition( $degree, 5 );
if( $condition{$parameter}{1}->() ) { ... }
It gets a little more tricky if you want the >=
(ie, the relationship itself) to be dynamically created as well. Then you have a couple of choices. One takes you back to stringy eval, with all of its risks (especially if you start letting your user specify the string). The another would be a lookup table within your generate_condition()
sub.
generate_condition()
returns a subroutine reference that when invoked, will evaluate the condition that was bound in at creation time.
Here's a generalized solution that will accept any of Perl's conditionals and wrap them along with the arguments being tested into a subroutine. The subref can then be invoked to evaluate the conditional:
use strict;
use warnings;
use feature qw/state/;
sub generate_condition {
my ( $test, $relation, $bound ) = @_;
die "Bad relationship\n"
if ! $relation =~ m/^(?:<=?|>=?|==|l[te]|g[te]|cmp)$/;
state $relationships = {
'<' => sub { return $test < $bound },
'<=' => sub { return $test <= $bound },
'==' => sub { return $test == $bound },
'>=' => sub { return $test >= $bound },
'>' => sub { return $test > $bound },
'<=>' => sub { return $test <=> $bound },
'lt' => sub { return $test lt $bound },
'le' => sub { return $test le $bound },
'eq' => sub { return $test eq $bound },
'ge' => sub { return $test ge $bound },
'gt' => sub { return $test gt $bound },
'cmp' => sub { return $test cmp $bound },
};
return $relationships->{$relation};
}
my $true_condition = generate_condition( 10, '>', 5 );
my $false_condition = generate_condition( 'flower', 'eq', 'stamp' );
print '10 is greater than 5: ',
$true_condition->() ? "true\n" : "false\n";
print '"flower" is equal to "stamp": ',
$false_condition->() ? "true\n" : "false\n";
Often when you construct these sorts of things one is interested in leaving one parameter open to bind at call-time rather than at subroutine manufacture-time. Let's say you only want to bind the "$bound
" and "$relation" parameters, but leave "$test
" open for specification at subroutine call time. You would modify your sub generation like this:
sub generate_condition {
my ( $relation, $bound ) = @_;
die "Bad relationship\n"
if ! $relation =~ m/^(?:<=?|>=?|==|l[te]|g[te]|cmp)$/;
state $relationships = {
'<' => sub { return $_[0] < $bound },
# ......
And then invoke it like this:
my $condition = generate_condition( '<', 5 );
if( $condition->(2) ) {
print "Yes, 2 is less than 5\n";
}
If the goal is to provide late binding of both the lefthand and righthand side in the relational evaluation, this will work:
sub generate_condition {
my $relation = shift;
die "Bad relationship\n"
if ! $relation =~ m/^(?:<=?|>=?|==|l[te]|g[te]|cmp)$/;
state $relationships = {
'<' => sub { return $_[0] < $_[1] },
'<=' => sub { return $_[0] <= $_[1] },
# ...... and so on .....
return $relationship->($relation);
}
my $condition = generate_condition( '<' );
if( $condition->(2,10) ) { print "True.\n"; }
This sort of tool falls into the category of functional programming, and is covered in beautiful detail in Mark Jason Dominus's book Higher Order Perl