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 ...
    }->();

    # ...
}

4 Comments

sub { ... }->() is often better spelled do { ... }.

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.

Leave a comment

About hanekomu

user-pic