CIT 042 Index > @_ and References

The @_ Array

The @_ array contains aliases for the arguments, not copies of their values! Try this code:

#!/usr/bin/perl
use strict;

my $height = 165;   # cm
my $weight = 65;    # kg

convert($height, $weight);

print "Height: $height in. Weight $weight lbs.\n";

#
#   Because the elements of @_ are aliases for
#   the arguments, changing $_[0] and $_[1] will
#   change $height and $weight
#
sub convert
{
    $_[0] = $_[0] / 2.54;   # convert to inches
    $_[1] = $_[1] * 2.2;    # convert to pounds
}

If you want to make sure that you don’t accidentally change variables in the caller, copy the values from @_ to another array or to individual variables:

sub do_not_touch
{
    # copies @_ into other variables;
    # values from calling program will not be changed
    #
    my ($copy1, $copy2) = @_;
    $copy1 *= 3;
    $copy2 += 4;
}

References

A reference is a scalar that holds the memory location and type of some Perl entity. The following code summarizes what we talked about in class. You are encouraged to copy and paste this into a file and give it a try.

#!/usr/bin/perl

#
#   Here are some variables
#
$scalar = 27;
@array = ( 10, 66 );
%hash = ( "Wladziu" => "Liberace" );

#
#   You use \ to get references to those variables
#
$scalar_ref = \$scalar;
$array_ref = \@array;
$hash_ref = \%hash;

#
#   If you print the references, you will see
#   that the reference has the variable's memory
#   address and data type.
#
print $scalar_ref, "\n";
print $array_ref, "\n";
print $hash_ref, "\n";

#
#   You use braces to dereference.
#   
print "${$scalar_ref}\n";
print "@{$array_ref}\n";
print %{$hash_ref}, "\n";

#
#   Getting to an individual element of an array or hash:
#   Notice that the expression starts with a $, because
#   an individual entry in an array or hash is a scalar.
#
print ${$array_ref}[0], "\n";   # prints number 10

#
#   The first set of curly braces dereferences $hash_ref.
#   The second set of curly braces indexes into the hash.
#
print ${$hash_ref}{"Wladziu"}, "\n"; # prints "Liberace"

#
#   All those curly braces can get difficult to read,
#   so you can use -> as a shortcut.
#   Here is a shortcut version of the previous lines:
#
print $array_ref->[0], "\n";
print $hash_ref->{"Wladziu"}, "\n";

So far, this just seems to be a more complicated way to access a variable, and it appears to be fairly useless. It does become useful in conjunction with passing parameters to a subroutine. For example, let’s say you want to write a subroutine to find the sum of the products of two lists of numbers (this is a common thing to do in engineering). That is, if we have the lists (7, 2, 3) and (5, 13, 11), the result would be 7*5 + 2*13 + 3*11, or 94. If the arrays aren’t the same length, it’s an error, so the subroutine should return zero.

Here is a program that will not work, because arrays get flattened:

#!/usr/bin/perl
use strict;

my @arr1 = (7, 2, 3);
my @arr2 = (5, 13, 11);
my $result = sum_of_products( @arr1, @arr2 );

sub sum_of_products
{
    my (@data1, @data2) = @_;
    my $i;
    my $sum;

    # debugging code:   
    print "Length of first array: ", scalar @data1, "\n";
    print "Length of second array: ", scalar @data2, "\n";

    if (scalar @data1  == scalar @data2 )
    {
        for ($i=0; $i < scalar @data1; $i++)
        {
            $sum += $data1[$i] * $data2[$i];
        }
    }
    else
    {
        $sum = 0;
    }
    return $sum;
}

However, if you pass references to the arrays, you will (effectively) be passing two different scalars in @_, and no flattening will take place:

#!/usr/bin/perl
use strict;

my @arr1 = (7, 2, 3);
my @arr2 = (5, 13, 11);

#
#   pass REFERENCES to the two arrays
#
my $result = sum_of_products( \@arr1, \@arr2 );
print "Result is $result\n";

sub sum_of_products
{
    my ($data_ref1, $data_ref2) = @_;
    my $i;
    my $sum;
    
    #
    #   This is the same code as before, except that
    #   we must now dereference every time we need to
    #   access the array.
    #

    #
    #   debugging code
    print "Length of first array: ", scalar @{$data_ref1}, "\n";
    print "Length of second array: ", scalar @{$data_ref2}, "\n";

    if (scalar @{$data_ref1}  == scalar @{$data_ref2} )
    {
        for ($i=0; $i < scalar @{$data_ref1}; $i++)
        {
            $sum += ${$data_ref1}[$i] * ${$data_ref2}[$i];
        }
    }
    else
    {
        $sum = 0;
    }
    return $sum;
}