Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.


  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • DownloadDownload
  • PrintPrint
Share this Page URL
Help

4. Subroutines > Persistent, Private Variables

Persistent, Private Variables

With my, you were able to make variables private to a subroutine, although each time you called the subroutine you had to define them again. With state, you can still have private variables scoped to the subroutine but Perl will keep their values between calls.

Going back to the first example in this chapter, you had a subroutine named marine that incremented a variable:

sub marine {
    $n += 1;  # Global variable $n
    print "Hello, sailor number $n!\n";
}

Now that you know about strict, you add that to your program and realize that your use of the global variable $n is now a compilation error. You can’t make $n a lexical variable with my because it wouldn’t retain its value between calls.

Declaring our variable with state tells Perl to retain the variable’s value between calls to the subroutine and to make the variable private to the subroutine. This feature showed up in Perl 5.10:

use 5.010;

sub marine {
    state $n = 0;  # private, persistent variable $n
    $n += 1;
    print "Hello, sailor number $n!\n";
}

Now you can get the same output while being strict-clean and not using a global variable. The first time you call the subroutine, Perl declares and initializes $n. Perl ignores the statement on all subsequent calls. Between calls, Perl retains the value of $n for the next call to the subroutine.

You can make any variable type a state variable; it’s not just for scalars. Here’s a subroutine that remembers its arguments and provides a running sum by using a state array:

use 5.010;

running_sum( 5, 6 );
running_sum( 1..3 );
running_sum( 4 );

sub running_sum {
  state $sum = 0;
  state @numbers;

  foreach my $number ( @_ ) {
    push @numbers, $number;
    $sum += $number;
  }

  say "The sum of (@numbers) is $sum";
  }

This outputs a new sum each time you call it, adding the new arguments to all of the previous ones:

The sum of (5 6) is 11
The sum of (5 6 1 2 3) is 17
The sum of (5 6 1 2 3 4) is 21

There’s a slight restriction on arrays and hashes as state variables, though. You can’t initialize them in list contexts as of Perl 5.10:

state @array = qw(a b c); # Error!

This gives you an error that hints that you might be able to do it in a future version of Perl, but as of Perl 5.14, you still can’t:

Initialization of state variables in list context currently forbidden ...