http://www.perlmonks.org?node_id=1208722

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

Hi there, I'm reading the variable $selectcriterium, either being '<' or '==' and then want to use them for a selection between two other variables also read in. Surely it must be possible to write the following code more effectively and shorter. I just failed to find the way. Can you advise me, please. Thanks, Gerd

#!/usr/bin/perl #These are the variables to be read in my $number1 = '2011'; my $number2 = '2011'; my $selectioncriterium = '=='; #This is the algorithm if ($selectioncriterium eq '<') { if ($number1 < $number2) { print "$number1 is smaller than $number2\n"; } elsif ($number2 < $number1) { print "$number2 is smaller than $number1\n"; } } elsif ($selectioncriterium eq '==') { if ($number1 == $number2) { print "$number1 is equal to $number2\n"; } else { print "$number1 is not equal to $number2\n"; } }

Replies are listed 'Best First'.
Re: Passing logical operators on as content of scalar
by Eily (Monsignor) on Feb 08, 2018 at 17:14 UTC

    Well, you might not do much better than that easily actually. The Dispatch table design pattern can help (you associate functions with some keys) which gives a basic example like that:

    use strict; use warnings; use feature 'say'; my %operators = ( '>' => sub { $_[0] > $_[1] }, '==' => sub { $_[0] == $_[1] }); sub test { my ($op, $left, $right) = @_; die "Unknown operator: $op" unless exists $operators{$op}; say "$left $op $right" and return if $operators{$op}($left, $right); say "$right $op $left" if $operators{$op}($right, $left); } test "==", 1, 1; test ">", 2, 3; test ">", 4, 5; test "<", 6, 7; __DATA__ 1 == 1 3 > 2 5 > 4 Unknown operator: < at pm_1208722.pl line 12.

    But if you want your own sentence to be written you'll need a more complex structure where for each key you have either several functions (one to compare, one to print) or a function and a template or something. I don't have the time to go any further than that at the moment though ^^".

Re: Passing logical operators on as content of scalar
by choroba (Cardinal) on Feb 08, 2018 at 19:10 UTC
    You might be interested in the comparison operator <=>:
    #!/usr/bin/perl use warnings; use strict; my $number1 = '2011'; my $number2 = '2011'; my $selectioncriterium = '=='; sub smaller { "$_[0] is smaller than $_[1]" } my %dispatch = ( 0 => sub { "$_[0] equals $_[1]" }, 1 => sub { smaller($_[1], $_[0]) }, -1 => sub { smaller($_[0], $_[1]) }, ); print $dispatch{ $number1 <=> $number2 }->($number1, $number2), "\n";
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      print $number1, " is ", ("less than", "equal to", "greater than")[($number1 <=> $number2) ++ 1], " ", $number2, "\n";
        print $number1, " is ", ("equal to", "greater than", "less than")[$number1 <=> $number2], " ", $number2, "\n";

        No need to add one :)

Re: Passing logical operators on as content of scalar
by Marshall (Canon) on Feb 08, 2018 at 19:37 UTC
    Your code as written is easy to understand at a glance. However, I think you are missing a case in the '<' situation, where $number1 is equal to $number2?

    I liked the post by Eily++. If I went that way, I likely would go further by incorporating the result of $number1 <=> $number2 into the hash key. That way there are no "if" statements at all in the calling code. Maybe resulting in a table like this?:

    my $dispatch = {"< -1" => "num1 less than num2", "< 0" => \&actionX, # blah...blah 6 or 9 entries to cover all cases, <, >, +== times 3 };
    In your "==" case, the dispatch action for $num1<$num2 and $num1>$num2 would be the same.
    Note: I would have to read the spec carefully on the spaceship operator. Instead of it returning -1,0,1 which is what it actually does, it might be spec'd as only negative number,0,positive number. I would allow whatever the spec says is allowed (convert to -1,0,1 so that hash lookup works no matter what exact spaceship implementation is).

    However having said that, the code structure that you have is easy to understand and it will execute very quickly (faster than a dispatch table). It took me a bit more "brainpower" to understand the Eily code whereas I understood your code at a glance. I wouldn't be overly concerned about "wordiness". If the pattern shown in your code repeats often elsewhere in the code, then the "understanding overhead" associated with the dispatch table is perhaps worth it. Simple, albeit it wordy code is often a good way to go. It is also possible that I might implement the three cases <,>,== with the same if logic (no special handling of ==). That would make the code even more wordy, but more "regular". Coding is part art and part science - lot's of ways to do things.

Re: Passing logical operators on as content of scalar
by Lotus1 (Vicar) on Feb 08, 2018 at 17:30 UTC

    You can get it to be shorter with the ternary operator. This replaces your elsif block.

    elsif ($selectioncriterium eq '=='){ print "$number1 is ", $number1 == $number2 ? '' : 'not ', "equal t +o $number2\n"; }

    For <, >, == it isn't as easy since there are three choices.

Re: Passing logical operators on as content of scalar
by Laurent_R (Canon) on Feb 08, 2018 at 22:05 UTC
    Really not sure whether this is any better, but you could use (string) eval, as shown here under the debugger:
    DB<1> $number1 = 2011; DB<2> $number2 = 2011; DB<3> $selectioncriterium = '==' DB<4> print "number1 is ", eval "$number1 $selectioncriterium $num +ber2" ? "" : "not ", "equal to $number2\n"; number1 is equal to 2011 DB<5> print "number1 is ", eval "$number1 $selectioncriterium 7842 +"? "" : "not ", "equal to 7842\n"; number1 is not equal to 7842
      Thanks for all the help and suggestions. Everything is valuable and has enlightened me.