Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Array of operators ...

by janDD (Acolyte)
on Sep 12, 2013 at 19:40 UTC ( #1053789=perlquestion: print w/ replies, xml ) Need Help??
janDD has asked for the wisdom of the Perl Monks concerning the following question:

Hi, is it possible to loop through an array of operators and "apply" them? Example:
my @ops = ("+","-","*","/"); foreach my $op (@ops){ print "4 $op 5 = "; }
I want the code to print
4+5=9 4-5=-1 4*5=20 4/5=0.8
I tried to define the operators as references ... but that didn't work: my @ops = (\+,\,\*,\/); Thanks for any help/idea Jan

Comment on Array of operators ...
Select or Download Code
Re: Array of operators ...
by Your Mother (Canon) on Sep 12, 2013 at 19:52 UTC

    Try something along these lines-

    ... my $statement = "4 $op 5"; print join(" = ", $statement, eval $statement), $/; ... __END__ 4 + 5 = 9 4 - 5 = -1 4 * 5 = 20 4 / 5 = 0.8

    eval can be dangerous since it executes any code so be careful with it.

Re: Array of operators ...
by tobyink (Abbot) on Sep 12, 2013 at 20:18 UTC

    You can't create a reference to an operator, but you can create a reference to a sub.

    my @term = (4, 5); my @ops = ( [ '+' => sub { $_[0] + $_[1] } ], [ '-' => sub { $_[0] - $_[1] } ], [ '*' => sub { $_[0] * $_[1] } ], [ '/' => sub { $_[0] / $_[1] } ], ); for my $op (@ops) { my ($symbol, $coderef) = @$op; printf( "%s %s %s = %s\n", $term[0], $symbol, $term[1], $coderef->(@term), ); }

    That's arguably a little cleaner and safer than using eval, but eval might be simpler and may be sufficient for your needs.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
      I used this exact approach when I was duplicating the PHP "date" function in Perl. Given, as you go to longer mathematical expressions, it'll probably be simpler to just pattern match for non-allowed characters (anything that isn't a number or limited set of operators) and then eval if everything looks ok.
Re: Array of operators ...
by abualiga (Scribe) on Sep 13, 2013 at 03:11 UTC
      looks like exactly the same task.

      Perl classes or interview question?

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        Plain curiosity in my case.

Re: Array of operators ...
by kcott (Abbot) on Sep 13, 2013 at 06:00 UTC

    G'day janDD,

    Here's another way to do it (built upon ++tobyink's solution).

    $ perl -Mstrict -Mwarnings -le ' { my %op_func = ( "+" => sub { $_[0] + $_[1] }, "-" => sub { $_[0] - $_[1] }, "*" => sub { $_[0] * $_[1] }, "/" => sub { $_[0] / $_[1] }, ); sub expr { my ($lhs, $op, $rhs) = split " ", shift; $op_func{$op}->($lhs, $rhs); } } my @ops = ("+","-","*","/"); foreach my $op (@ops){ print "4 $op 5 = ", expr("4 $op 5"); } ' 4 + 5 = 9 4 - 5 = -1 4 * 5 = 20 4 / 5 = 0.8

    -- Ken

      Thanks a lot to everyone ... :)

      This also works nicely with state built-in of 5.10+ for tighter encapsulation, compile-time evaluation of hash:

      >perl -wMstrict -le "use 5.010; ;; sub expr { state $op_func = { '+' => sub { $_[0] + $_[1] }, '-' => sub { $_[0] - $_[1] }, '*' => sub { $_[0] * $_[1] }, '/' => sub { $_[0] / $_[1] }, }; ;; my ($lhs, $op, $rhs) = split ' ', shift; return $op_func->{$op}->($lhs, $rhs); } ;; for my $op (qw(+ - * /)) { my $exp = qq{4 $op 5}; print $exp, ' = ', expr($exp); } " 4 + 5 = 9 4 - 5 = -1 4 * 5 = 20 4 / 5 = 0.8

        For my own code (I'm running 5.18.1), I probably would have used state as you've done there: your post got a ++ from me. However, unless the question specifically involves a more recent Perl, or I know the person I'm responding to is familiar with 5.10.0+, I find providing a pre-5.10 (or, more strict, an any Perl5) solution to be a lot less hassle: that would be non-virtuous laziness on my part. :-)

        While there may be some subtlety I'm missing, which I'm more than happy to learn about, I really don't see any "tighter encapsulation". In both cases, the hash is only evaluated once and only visible by the expr subroutine. From a visual perspective, the hash is clearly more closely associated with the subroutine: in fact, that would be one of my reasons for preferring it.

        There's another issue which I'd forgotten about until I ran a few tests. You can't simply substitute my with state in my %op_func = (...);. If you try this, you get a compilation error:

        "Initialization of state variables in list context currently forbidden"

        So, you need to code state $op_func = {...}; as you have here. Having to dereference $op_func incurs a minimal overhead which, in general, wouldn't prevent me from using it unless efficiency was of particular concern. I found the %op_func solution to be consistently 2% faster than the $op_func solution:

        $ perl -Mstrict -Mwarnings -E ' use Benchmark qw{cmpthese}; { my %op_func1 = ( "+" => sub { $_[0] + $_[1] }, "-" => sub { $_[0] - $_[1] }, "*" => sub { $_[0] * $_[1] }, "/" => sub { $_[0] / $_[1] }, ); sub expr1 { my ($lhs, $op, $rhs) = split " ", shift; $op_func1{$op}->($lhs, $rhs); } } sub expr2 { state $op_func2 = { "+" => sub { $_[0] + $_[1] }, "-" => sub { $_[0] - $_[1] }, "*" => sub { $_[0] * $_[1] }, "/" => sub { $_[0] / $_[1] }, }; my ($lhs, $op, $rhs) = split " ", shift; $op_func2->{$op}->($lhs, $rhs); } my @ops = ("+","-","*","/"); cmpthese(-1, { with_my => sub { expr1("4 $_ 5") for @ops }, with_state => sub { expr2("4 $_ 5") for @ops }, }); ' Rate with_state with_my with_state 112733/s -- -2% with_my 114840/s 2% --

        -- Ken

Re: Array of operators ...
by LanX (Canon) on Sep 13, 2013 at 11:16 UTC
    IMHO an easier compromise between elegant eval and security issues is a hash with '%allowed_ops':

    DB<137> %allowed_ops=map { $_=>$_} qw# + - * / # => ("+", "+", "-", "-", "*", "*", "/", "/") DB<138> $op ="+" => "+" DB<139> $statement = "4 $allowed_ops{$op} 5"; => "4 + 5" DB<140> print "$statement = ", eval $statement => 1 4 + 5 = 9

    you can also explicitly check for exists in the hash to catch errors beforehand and you are free to "invent" new operators.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1053789]
Approved by tobyink
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2014-08-02 04:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Who would be the most fun to work for?















    Results (54 votes), past polls