3

Just wanted to know what was the best way to reserve the value of a variable across multiple calls to the same subroutine . i.e

$someList  = &someRoutine(100, 200);
$someList2 = &someRoutine(100, 200);

sub someRoutine {
    $someAddition = 0;
    foreach $someSum (@_){
        $someAddition += $someSum;
    }
    return $someAddition
}

print $someList;
print $someList2;

Basically, someList should print 300 and someList2 should print 600. How do i make it so that someList2 prints 600? i want $someAddition to be preserved across multiple subroutine calls.

3 Answers 3

6

There are several ways to do it. I'll demonstrate two of them:

First, in modern versions of Perl you can use state:

use feature qw/state/; 

print someRoutine(100,200), "\n";
print someRoutine(100,200), "\n";

sub someRoutine {
    state $someAddition = 0;
    foreach my $someSum ( @_ ) {
        $someAddition += $someSum;
    }
    return $someAddition;
}

In this version, the $someAddition variable will be initialized to zero once, and only once. From that point on, the value will be retained between calls.

Another version is using a lexical closure. Here's an example:

my $summer = makeSummer();
print $summer->(100,200), "\n";
print $summer->(100,200), "\n";

sub makeSummer {
    my $someAddition = 0;
    return sub {
        $someAddition += $_ foreach @_;
        return $someAddition;
    }
}

The second version is a little more complex, but has two advantages. First, you can start a fresh summation simply by calling the makeSummer routine to manufacture a new closure. Second, it will work on any version of Perl 5, not just versions recent enough to have the newer state keyword.

If you are not concerned with initializing the stateful variable before the sub is declared, you can also do this:

my $someAddition;
sub someRoutine {
    $someAddition = 0 unless defined $someAddition;
    foreach my $someSum( @_ ) {
        $someAddition += $someSum;
    }
    return $someAddition;
}

A fourth method makes use of package globals. I save this one for last because it's the most prone to abuse and mistakes. But here you go;

our $someAddition = 0;

someRoutine(100,200);
print "$someAddition\n";

someRoutine(100,200);
print "$someAddition\n";

sub someRoutine {
    $someAddition += $_ foreach @_;
}

In this last version, $someAddition is a package global, and its global scope makes it available both inside and outside of any subroutines living within the same namespace.

Sign up to request clarification or add additional context in comments.

9 Comments

Thank you for your answers. Unfortunately, the perl version we use at our school is pretty old so state would not work, also, i need to have the subroutine return an array with a multitude of items in the array, so i don't know whether the lexical closure one will work. Is there any other way?
return ( $someAddition, [@array] ); Perl's return values don't have to be homogeneous entities.
Sorry if i didn't explain it correctly , but $someAddition is part of the array i am returning.
Then the closure method should work. But if you prefer using package globals instead, see the third example, provided in my most recent edit.
|
5
  • I assume you're at least using a variant of Perl 5? It has been bad practice to use ampersands & on subroutine calls since the first version of Perl 5 twenty-two years ago.

  • It is also vital that you use strict and use warnings at the top of every Perl program, and declare your variables ay their first point of use with my. It is a measure that will uncover many simple coding errors that you can otherwise easily overlook.

  • Perl variable names should use only lower-case letters, digits, and underscores. Capital letters are reserved for global identifiers such as package names.

By far the simplest and most common way of creating a static variable is just to declare it outside the subroutine. Like this

use strict;
use warnings;

my $some_list  = some_routine(100, 200);
my $some_list2 = some_routine(100, 200);

my $some_addition;

sub some_routine {
  $some_addition += $_ for @_;
  return $some_addition
}

print $some_list, "\n";
print $some_list2, "\n";

output

300
600

If you want to protect the variable from being accessed by any following code other than the subroutine, then just enclose them in braces, like this

{
  my $some_addition;

  sub some_routine {
    $some_addition += $_ for @_;
    return $some_addition
  }
}

Comments

3

Take a look at Persistent Private Variables in "man perlsub".

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.