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% --
|