Pointers in Perl

We have a big restriction on elements of arrays and hashes in Perl. Both hashes and arrays may contain only scalar variables. To overcome that restriction we will use pointers. To understand how to use pointers we first need to understand what is a pointer and how Perl stores values of variables.

Pointers

Perl associates any defined variable with a name and an address in the memory. (The value of this variable is stored at this location). A pointer is a scalar variable that contains an address. The value stored at this address is called referent. The referent may be one of the built-in types (scalar, hash, array, pointer, or be a user defined type based on one of these types). Since pointer may refer to values of different types we need to distinguish array pointers from hash pointers, etc.

To get a reference (pointer) from an object we need to use the back-slash operator:
Pointer at Named object
scalar \$scalar
array \@array
hash \%scalar
function \&scalar
In the following example we create an array @data and pointer $ref, then we use this pointer to get elements of the array:

my @data = qw / This is an array of data /;
my $ref = \@data;
for($i=0;$i<=$#data;$i++){
  print $ref->[$i] . "\n";
}
To get an element from an array referenced by a pointer we use a new operator ->. Thus, $ref->[2] gives us the element of the array pointed at by $ref with index 2. We can also syntax like $$ref[2] to reach the same element. We can think of this syntax like that since $ref points to an array @data we will use $ref instead of the name of the array data. That is, instead of using $data[2] we use $$ref[2]. The same rule works for hashes as well:
my %hash = (name => "Bob", city => "Atlanta", zip => "30033");
my $ref = \%hash;
foreach ( keys(%hash) ){
  print "$_ => $$ref{$_} \n";
}
The only thing we need to remember reaching hash elements through a reference is to use curly brackets instead of square brackets.

Anonymous data

Pointers to existing data may be convenient when we need to send some data to a function, but they are not very useful in dynamic programming. Sometimes we need to create an array of data or a hash, but we do not want to name it. All we need to do is just put that data in a memory and remember the pointer to the location. In Perl we can create pointers to anonymous arrays and hashes. By using the following syntax we: To create a pointer to an anonymous array use the following syntax
$pointer = [ array_elements ];
Similar syntax works for pointers to anonymous hashes
$pointer = { hash_keys_and_elements };
The following example illustrates the syntax
# create a new anonymous array and store the pointer to it at ref
$ref = [qw / This is another array (anonymous) /];
foreach ( @$ref ){
  print "$_\n";
}

# create a new anonymous hash and store the pointer to in ref
$ref = { this => "is", another => "hash", an => "anonymous"};
foreach ( keys(%$ref) ){
  print "$_ => $ref->{$_} \n";
}

Array of hashes

In this section we would like to create a more complicated data structure than we used before. Assume that we need to read a file like:
Bob 4.0 Atlanta
Bill 3.5 Boston
Sam 2.0 Chicago
where each line contains some data for one student. We need to read all the data and keep it in one array. The difficult part is the data itself contains name, GPA, and city. Thus, we need to have an array of hashes. We cannot have an array of hashes, but we can have an array of pointers to hashes. The solution to the problem is the following, we read a line from file, split it into three pieces (name, GPA, and city), create an anonymous hash and store the pointer to that has in the next array element:
my @student = (); # create an empty array
my $ind = 0;      # initialize the counter
while (<>){
   chomp;
   ($name, $GPA, $city) = split(/\s+/, $_);
   # create an anonymous hash and save the pointer to it in the array element
   $student[$ind] = { name => $name, GPA => $GPA, city => $city };
   $ind++;
}
for($i=0;$i<$ind;$i++){
   printf "%2d: %10s %.2f %10s\n", $i+1, $student[$i]->{name}, $student[$i]->{GPA}, $student[$i]->{city};
}
As you can see each element of the array is a pointer to a hash.

Hash of arrays

Another problem we can run into is to have a hash each element of which is an array. In the other words, we would like to associate not just one scalar variable with a key ford, but a whole list of scalar values. The idea of the solution is exactly the same as in the previous section. Instead of keeping scalar values in our hash we will keep pointers to an array.

For example, let we have a file like this:

Bob: 89 76 89 90 100 101
Bill: 100 89 76 80 34 0
Sam: 102 99 87 78 90 69
For each student mentioned in the file we would like to keep her or his grades. To do that we will write the following code:
%grades = ();    # create an empty hash
while( <> ){
   chomp;
   ($name, $grds) = split(/:\s*/, $_);
   $grades{$name} = [ split(/\s+/, $grds) ];
}

for ( keys(%grades) ){
   printf "%8s : %.2f\n", $_, avg( @{$grades{$_}} );
}

sub avg
{
  my @arr = @_;
  my $sum = 0;
  my $count = $#arr + 1;
  for(my $i=0;$i<$count;$i++){
     $sum += $arr[$i];
  }
  $sum/$count;
}
The very first loop just reads the file line by line and splits the line into name and grades, then it uses split function again to get an array of grades, then in the assignment $grades{$name} = [ split(/\s+/, $grds) ]; we initialize an element of the has with the key $name and the value is a pointer to an anonymous array that contains all the grades for the student. To create a pointer we use square brackets around the functions split. In the second loop we call function avg to compute the average grade for each student. Please notice that we use @ in front of the hash element to specify that we pass an array not a pointer to the function.


For more information I would recommend the wonderful book Perl Cookbook from Tom Christiansen and Nathan Torkington. See chapter 11 for more details about Perl records and pointers.