Perl v5.28 allows you to initialize array and hash variables that you declare with state
. This is a feature a long time coming and that I’m quite happy as finally arrived.
Since v5.10 and up to v5.26 you could only initialize a state
variable if it was a scalar. You could declare a hash or array variable but you couldn’t give it an initial value at the same time. You could do this:
use v5.26; sub do_it { state $previous_argument = 0; ... }
But you can’t give an array or hash initial values. You can’t assign to them at all. “Initializing” them to an empty list won’t work:
use v5.26; sub do_it { state @previous_arguments = (); # ERROR push @previous_arguments, @_; ... }
The error gives some hope that the situation will change:
Initialization of state variables in list context currently forbidden
References are one way around this because they are scalar variables. Instead of an array or hash, use an array or a hash reference.
use v5.26; sub do_it { state $previous_arguments = []; push @$previous_arguments, @_; ... }
Since you can initialize the array reference you can give it values before the actual arguments of the first call:
use v5.26; sub do_it { state $previous_arguments = [ qw(this that) ]; push @$previous_arguments, @_; ... }
With v5.28 you can skip the references and the dereferencing. This is slightly cleaner and amenable to people still grappling with the idea of references:
use v5.28; sub do_it { state @previous_arguments = qw( this that ); push @previous_arguments, @_; ... }
Fibonacci caching
In Mastering Perl I show a caching Fibonacci generator. If you intend on using many of these numbers you don’t want to recompute what you already know. You can trade a bit of memory for some speed by caching prior work. Here’s the same thing using an array variable:
use v5.28; foreach my $n ( 0 .. 100 ) { say fibonacci( $n ); } sub fibonacci { state @Cached = qw( 1 1 ); my( $n ) = @_; return $Cached[$n] if $n <= $#Cached; foreach my $i ( $#Cached + 1 .. $n ) { $Cached[$i] = $Cached[-1] + $Cached[-2]; } return $Cached[$n]; }
Here's that same subroutine prior to v5.28. It's a bit more ugly although perfectly serviceable. It uses an array reference so there are some added dereferencing arrows and an accretion of symbols to get the last index ($#$Cached
):
use v5.26; sub fibonacci { state $Cached = [ qw( 1 1 ) ]; my( $n ) = @_; return $Cached->[$n] if $n <= $#$Cached; foreach my $i ( $#$Cached + 1 .. $n ) { $Cached->[$i] = $Cached->[-1] + $Cached->[-2]; } return $Cached->[$n]; }
Shouldn’t features refaliasing and declared_refs allow to achieve this, too:
state \@a = [];
For me, it didn’t work. @a seems to lose its state…