Perl

07. References and Data Structures


References

Perl's "references" let you create pointers to variables, subroutines, constants, anonymous data structures, anonymous subroutines, etc all using the same notation.

A reference is a scalar, it is stored in a scalar variable like a string or number. A collection of them can be stored in an array or a hash as well.

Perl does all the necessary reference counting and garbage collection on everything, so you don't have to worry about dynamic allocation issues.


Creating/Accessing References

Create reference to a variable or value by backslashing the value to be referred to. Use dollar-name with the type indicator to de-reference.

$a = 7;
$aref = \$a;      # scalar aref now reference to scalar a

print $aref;      # prints SCALAR(0x80bf9a8) or some such nonsense
print $$aref;     # prints 7

$$aref += 2;      # $a now 9

@b = (1, 2, 3);
$bref = \@b;      # scalar bref now reference to array b

print $$bref[0];  # prints 1, $bref for ref name, another $ to access one element

$numref = \10;            # scalar numref now referencd to value 10
$strref = \"hi there";    # scalar strref now referencd to value "hi there"

$subref = \&one;  # reference to sub named "one"
&$subref;         # invoke sub via ref
Can use them anywhere you'd use the regular variable
push(@$bref, 4, 5);
print "$b[3], $$bref[4]\n",   # prints 4, 5
Alternative, easier notation for deref'ing with arrays and hashes on a single value:
print "$bref->[0]\n";

print "$href->{"name"}\n";
The function ref will test a scalar and see if it is a reference or not. Will return false if the scalar contains a number or string, otherwise a string telling you what kind of reference it contains:

References and sub

Note how the arrays passed to sub get "flattened" into single array.

#!/usr/bin/perl -w

sub printer {
   foreach $element (@_) {
      print "$element\n";
   }
}

@one = (1, 2, 3);
@two = (8, 9, 10);

printer @one, @two;

Solve this by using references in passing our data.

#!/usr/bin/perl -w

sub printer {
   foreach $element (@_) {
      print "array start\n";
      foreach $item (@$element) {
         print "$item\n";
      }
   }
}

@one = (1, 2, 3);
@two = (8, 9, 10);

printer \@one, \@two;

Anonymous Data

Scalars:
$refvalue = \10;
Arrays (note square brackets):
$refarray = [1, 3, 5];         # anonymous array created with 3 values

foreach $value (@$refarray) {
   print $value;
}
Hashes (note curly braces):
$refhash = {"key1", "value1", "key2", "value2"}; # anon hash with 2 k/v pairs

foreach $key (sort reverse keys %$refhash) {
   print "$$refhash{$key}\n";                  # $$ for one value through a reference
}

Reference Counts and Scope

Perl does reference counting and garbage collection.

Things that go out of scope don't necessarily go away right away, wait until reference count goes to zero.

{
   my $x = 7;         # declare and use $x in a block with my
   $x++;

   $xref = \$x;       # create reference to it
}

print "x is now: $x\n";        # errors: "main::x only used once"
                               #         "Use of uninitialized value"

print "x is now: $$xref\n";    # ok: can use ref.

Multi-Level References

$a = 10;
$aref = \$a;
$arefref = \$aref;
$arefrefref = \$arefref;

print "please shoot me with $$$$arefrefref bullets if I ever write this code\n";

Two-Dimensional Arrays

#!/usr/bin/perl -w

# create array of references to anonymous arrays
@array2d = ( [1, 2, 3],
             [4, 5, 6],
             [7, 8, 9]);

print "center value is: $array2d[1]->[1]\n";
print "center value is: $array2d[1][1]\n";

Can create things piece-by-piece, autovivification.

$arrayfoo[1][2] = 7;      # arryfoo springs into existance with an
                          # undef value and a ref to another anon array
                          # with 3 elmts: 2 undefs and a 7

Exercise: imagine putting more than just numbers in each position, say another array or a hash as more references...


Structures Using References

Can create complex structures using references.

#!/usr/bin/perl -w

%greg = ('name' => "Greg",               # not reference
         'age' => 31);
%bridget = ('name' => "Bridget Fonda",   # not reference
            'age' => 36);
%winona = ('name' => "Winona Ryder",     # not reference
           'age' => 29) ;

@wives = (\%bridget, \%winona);   # create list of desired wives, references

$greg{'wives'} = \@wives;         # assign to rightful person, reference

print "wife one is: $greg{'wives'}->[0]->{'name'}\n";
print "wife two is: $greg{'wives'}[1]{'name'}\n";

Or we can do it with only a single named variable and lots of anonymous data.

#!/usr/bin/perl -w

%greg = ('name'  => "Greg",
         'age'   => 31,
         'wives' => [
                      {
                        'name' => "Bridget Fonda",
                        'age' => 36
                      },
                      {
                        'name' => "Winona Ryder",
                        'age' => 29
                      },
                    ]
        );


print "wife one is: $greg{'wives'}->[0]->{'name'}\n";
print "wife two is: $greg{'wives'}[1]{'name'}\n";

Data::Dumper

Use standard Data::Dumper module to help understand what is in your code. You can read it's output and "eval" it back into your code.

#!/usr/local/bin/perl -w

use Data::Dumper;

$foo = [ { name => 'Greg',
           age => 32, },
         { name => 'Winona',
	   age => 29, },
   ];

print "$foo->[0]{age}\n";

print Data::Dumper->Dump([$foo], ["foo"]);   # list of data, list of names

output:
32
$foo = [
         {
           'age' => 32,
           'name' => 'Greg'
         },
         {
           'age' => 29,
           'name' => 'Winona'
         }
       ];