Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Re: Array of operators ...

by kcott (Archbishop)
on Sep 13, 2013 at 06:00 UTC ( [id://1053844]=note: print w/replies, xml ) Need Help??


in reply to Array of operators ...

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

Replies are listed 'Best First'.
Re^2: Array of operators ...
by AnomalousMonk (Archbishop) on Sep 13, 2013 at 08:44 UTC

    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

        ... tighter encapsulation ...

        I doubt any of this will be new to you, but just to clarify my thoughts for myself, I suppose what I really had in mind was actually two separate and disparate effects:

        • If a 'private' closure is not further encapsulated in a use-d module, pesky order-of-evaluation effects can manifest:
          >perl -wMstrict -le "print 'before: ', defined S() ? S() : 'undefined'; ;; { my $x = 42; ;; sub S { return $x; } } ;; print 'after: ', defined S() ? S() : 'undefined'; " before: undefined after: 42
        • If such a closure is encapsulated in a used-d module, the order-of-evaluation problems go away (assuming you're not doing any fancy-schmancy CTFE in the module, I think this is reliably true), but the 'tighter' aspect, strictly speaking, then kicks in: a state variable really is private to the function in which it's defined, but 'private' closures allow the sharing of access to variables among several functions — in itself, a highly useful property in certain circumstances!
          >perl -wMstrict -le "print M::get_x(); ;; use M; ;; print M::get_x(); " 42 42
          Where M.pm is:
          package M; { my $x = 42; sub get_x { return $x; } sub set_x { return $x = $_[0]; } } my $y = 1729; sub cannot_access_x { return $y; } 1;

        Oh, and:

        ... consistently 2% faster ...

        I'm not sure I really believe in a 2% performance difference reported by Benchmark, but as long as it's you...!

Re^2: Array of operators ...
by janDD (Acolyte) on Sep 13, 2013 at 06:49 UTC
    Thanks a lot to everyone ... :)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1053844]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (3)
As of 2024-04-19 21:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found