问题
First timer...so let me know if there is anything that I have not paid attention to whilst posing a question.
The question is how to use a scalar as a condition, as the code below does not work.
my @parameter=('hub');
my %condition;
$condition{'hub'}{'1'}='$degree>=5';
foreach (@parameter) {
if ($condition{$_}{'1'}) {..}
}
I thought that is because the condition is not interpreted correctly, so I also tried the following, which also did not work.
if ("$condition{$parameter}{'1'}") { ..}
Would really appreciate any help. :)
回答1:
You either want string eval, which evaluates a string as Perl code
if (eval $condition{$_}{'1'}) { ...
or perhaps a more secure approach would be using code references
$condition{'hub'}{'1'} = sub { return $degree>=5 };
if ($condition{$_}{'1'}->()) { ...
In the second example, you are attaching a piece of code to a variable. The $var->()
syntax executes the code and evaluates to the return value of the code.
回答2:
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
回答3:
What are you expecting? String values are interpreted as true
when they are nonempty.
themel@kallisti: ~ $ perl -e 'print "oops\n" if "false" ; '
oops
themel@kallisti: ~ $ perl -e 'print "oops\n" if "" ; '
themel@kallisti: ~ $ perl -e 'print "oops\n" if "\$degree < 5" ;'
oops
If you want to dynamically evaluate code in your conditions, you have to investigate eval. Example:
my @conds=('$foo>42', '$foo>23');
my $foo = 33;
foreach my $cond(@conds) {
print "$cond itself was true\n" if $cond;
print "$cond evaluated to true\n" if eval($cond);
}
prints
$foo>42 itself was true
$foo>23 itself was true
$foo>23 evaluated to true
来源:https://stackoverflow.com/questions/11976045/using-a-scalar-as-a-condition-in-perl