Home » Tutorials » References |
Shlomi Fish
Perl allows one to refer to the location of a certain variable in memory. An expression that holds such location is called a reference. Those that are familiar with C's or Pascal's pointers may think of references as pointers. There are however, two fundamental differences:
Perl distinguishes between an array or a hash and a reference of it. The reference of any array may be taken, and a reference to an array may always be converted to its elements, but there is still a difference in functionality.
The best way to change a variable in a different scope (such as inside a different function) is to pass its reference to the function. The called function can then dereference the variable to access or modify its value.
In this example, which is intended to give a taste of the capabilities of references, we will solve the well-known Towers of Hanoi problem. The number of disks can be inputted from the command-line. The towers themselves will be represented as an array of three elements, each of which is a reference to an array.
Here goes:
#!/usr/bin/perl use strict; use warnings; my $num_disks = shift || 9; my @towers = ( [ reverse(1 .. $num_disks) ], # A [ ... ] is a dynamic reference to [ ], # an array [ ] ); sub print_towers { for(my $a=0;$a<3;$a++) { print ": "; print join(" ", @{$towers[$a]}); # We de-reference the tower print "\n"; } print "\n\n"; } sub move_column { my $source = shift; my $dest = shift; my $how_many = shift; if ($how_many == 0) { return; } # Find the third column my $intermediate = 0+1+2-$source-$dest; move_column($source, $intermediate, $how_many-1); # Print the current state print_towers(); # Move one disk. Notice the dereferencing of the arrays # using @{$ref}. push @{$towers[$dest]}, pop(@{$towers[$source]}); move_column($intermediate, $dest, $how_many-1); } # Move the entire column move_column(0,1,$num_disks); print_towers();
In Perl the backslash (\
) serves as an operator that return the
reference to an existing variable. It can also return a dynamic reference
to a constant.
Here is an example that uses a reference to update a sum:
#!/usr/bin/perl use strict; use warnings; my $sum = 0; sub update_sum { my $ref_to_sum = shift; foreach my $item (@_) { # The ${ ... } dereferences the variable ${$ref_to_sum} += $item; } } update_sum(\$sum, 5, 4, 9, 10, 11); update_sum(\$sum, 100, 80, 7, 24); print "\$sum is now ", $sum, "\n";
As can be seen, because the reference to $sum
was used, its value
changes throughout the program.
An array surrounded by square brackets ([ @array ]
) returns a dynamic
reference to an array. This reference does not affect other values directly, which is
why it is called dynamic.
We have already encountered such dynamic array references in the Hanoi example. But their use is not limited to such. Since a reference to an array is a scalar, it can serve as a hash value and therefore serve as an object member. (as will be seen later in the series).
In this example, a function is given two arrays, and returns an array that is the element-wise sum of both of them:
#!/usr/bin/perl use strict; use warnings; sub vector_sum { my $v1_ref = shift; my $v2_ref = shift; my @ret; my @v1 = @{$v1_ref}; my @v2 = @{$v2_ref}; if (scalar(@v1) != scalar(@v2)) { return undef; } for(my $i=0;$i<scalar(@v1);$i++) { push @ret, ($v1[$i] + $v2[$i]); } return [ @ret ]; } my $ret = vector_sum( [ 5, 9, 24, 30 ], [ 8, 2, 10, 20 ] ); print join(", ", @{$ret}), "\n";
This code produces the following output:
13, 11, 34, 50
The fundamental difference between using \@myarray
on an existing
variable named @myarray
to using [ @myarray ]
is this: the latter
forms creates a dynamic copy of @myarray
and if this copy changes,
@myarray
will not change with it. On the other hand, changes made to
a reference generated by backslash, will affect the original variable.
Note that if the members of @myarray
are themselves references, then
the second form will not make a copy of them. Thus, they can be modified too,
even in the second form.
In a similar way to the square brackets, putting a hash inside a pair
of curly brackets ({ ... }
) will make it into a reference to a
hash. Like an array reference, this reference is a scalar and can be
used as an array element or a hash value. Plus, its own values can be
references to other arrays or hashes.
To demonstrate this let's see the code of part of the contents table of this very lecture, to demonstrate the multi-level data structure capabilities of perl:
#!/usr/bin/perl use strict; use warnings; my $contents = { 'title' => "Contents", 'subs' => [ { 'url' => "for", 'title' => "The for loop", 'subs' => [ { 'url' => "next.html", 'title' => "Behaviour of next in the for loop", }, { 'url' => "whence_for.html", 'title' => "Whence for?", }, ], }, { 'url' => "hashes", 'title' => "Hashes", 'subs' => [ { 'url' => "functions.html", 'title' => "Hash Functions", }, ], }, { 'url' => "my", 'title' => "Declaring Local Variables with \"my\"", 'subs' => [ { 'url' => "use_strict.html", 'title' => "\"use strict\", Luke!", }, ], }, { 'url' => "argv.html", 'title' => "The \@ARGV array", }, ], 'images' => [ { 'url' => 'style.css', 'type' => 'text/css', } ], }; sub get_contents { return $contents; }
The entire scalar or data structure pointed to by the reference can be
retrieved by dereferencing. Dereferencing is done by using a $
,
a @
or a %
(depending if the reference refers to a scalar
, array or a hash respectively), and then the reference inside curly brackets.
Here are some simple examples:
#!/usr/bin/perl use strict; use warnings; my $ds1 = { 'h' => [5,6,7], 'y' => { 't' => 'u', 'o' => 'p' }, 'hello' => 'up', }; my $array_ref = [5, 6, 7, 10, 24, 90, 14]; my $a = "Hello World!"; my $b = \$a; print "\$array_ref:\n"; print join(", ", @{$array_ref}), "\n"; print "\n\n\$ds1->{'h'}:\n"; print join(", ", @{$ds1->{'h'}}), "\n"; my %hash = %{$ds1->{'y'}}; print "\n\n\%hash:\n"; foreach my $k (keys(%hash)) { print $k, " => ", $hash{$k}; } print "\n\n\$\$b:\n"; print ${$b}, "\n";
If the expression that yields the reference is a simple one than the curly
brackets can be omitted (e.g: @$array_ref
or $$ref
).
However, assuming you use curly brackets - the expression surrounded
inside them can be as complex as you would like.
Dereferencing in Perl Perl for Perl Newbies - Lecture Series perldoc perldsc (for detailed examples of perl data structures) perldoc perldata