Caching multi-statement computations using an anonymous subroutine
Suppose that a subroutine gets called several times and that in the subroutine
you need to use some computational result that stays the same for every call.
You could use a state
variable or an our
variable.
sub foo {
# ...
state $some_value //= ... some computation ...;
# ...
}
Using the defined-or operator, the value will only be computed the first time the subroutine is called.
But what if the computation takes several statements? You could move it to its own subroutine and call it with a single statement, or you might resort to something like:
# NOT IDIOMATIC
sub foo {
# ...
state $some_value;
unless (defined $some_value) {
# ... some more ...
# ... intensive ...
# ... computation ...
$some_value = ...;
}
# ...
}
The problem is that we need to write $some_value
three times.
But a more idiomatic way is to use an anonymous subroutine and call it in-place:
sub foo {
# ...
state $some_value //= sub {
# ... some more ...
# ... intensive ...
# ... computation ...
}->();
# ...
}
sub { ... }->()
is often better spelleddo { ... }
.One of the Items in Effective Perl Programming is "Use do {} to create inline subroutines". There are many other ways you can use do to create a scope and avoid retyping variables.
Could be useful, but many times an 'intensive computation' would be dependent on some arguments passed to sub foo. In that case this approach isn't applicable or am I missing something?
I don't think the "//=" is required, it could just be "="?
perl -E 'sub i { state $i = 0; $i++; say $i; } i() for 1 .. 10;'
1
2
3
4
5
6
7
8
9
10
The "$i = 0;" bit is clearly only called the first time, or that would print 10 "1"s.