Tweet

Introduction to References

Shlomi Fish

Introduction

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:

  1. There is no reference arithmetics in perl. If for example, a reference points to the fifth element of an array, then adding 1 to it will not refer you to the sixth element. In fact, adding or subtracting integers from references is possible but quite meaningless.

  2. A reference is guaranteed to remain the same even if an array or a string are resized. In C, reallocing an array yields a completely different pointer.

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.

Example: The Towers of Hanoi

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();

\ - taking a reference to an existing variable

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.

[ @array ] - a dynamic reference to an array

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

Note

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.

{ %hash } - a dynamic reference to a hash

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;
    }

Dereferencing

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.

See Also

    Dereferencing in Perl
    Perl for Perl Newbies - Lecture Series
    perldoc perldsc (for detailed examples of perl data structures)
    perldoc perldata
[Top]