Initialize array and hash variables with state

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];
	}
Leave a comment

1 Comments.

  1. 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…

Leave a Reply


[ Ctrl + Enter ]