Team LiB   Previous Section   Next Section

Perl References

The big difference between Perl 4 and Perl 5 was the introduction of references, which made object orientation possible. You can think of references as being a kind of pointers, locators, or remote tracking devices. They are the glue spot trails sticking Perl 5 data structures together. Think of ET pointing the way home, Indiana Jones standing on the X marking the spot in Venice, or James Bond trailing Goldfinger's car with a remote tracking device. The bony finger, the X, or the beeper are all references to remote information. So how does Perl point to its own vital information? In structure, references are simple scalars holding two vital pieces of information:

  • What kind of thing am I pointing at?

  • Where is the thing I'm pointing at located (in hexadecimal memory)?

These two pieces of information can be seen in Figure A-4, stored under each of our three references. The first refers to a scalar, the second to an array, and the third to a hash. Each reference holds the variable type it's referring to and its memory address. We can see this for ourselves if we create three similar references, and then print them out:

$camel = "Asimov";
@camel = ("Foundation and Earth", "I, Robot", "Nightfall");
%camel = (Emperor => "Cleon",
          CouncilMan => "Trevize",
          Robot => "R. Daneel Olivaw");
  
$scalar_ref = \$camel;  # References created by backslashing
$array_ref = \@camel;   # the original variable.
$hash_ref = \%camel;
  
print $scalar_ref, " ", $array_ref, " ", $hash_ref, "\n";
Figure A-4. The glue of Perl references
figs/pdba_aa04.gif

Notice that each original variable is named camel, but this causes absolutely no clash in Perl because scalars, arrays, and hashes are all different variable types, in the same way that Homer Simpson, Springfield philosopher, and Homer the Greek, Trojan chronicler, are different types of people, despite possessing the same name. When executed, the print statement shown above produced memory address traces like these:

SCALAR(0x457c3f4) ARRAY(0x457f420) HASH(0x457f468)

We can now take these three references and go back along their arrows, to get the original information back out again:

print "SCALAR: ${$scalar_ref} \n"; # Isolate reference with braces
                                   # then dereference with a $ symbol.
  
print "ARRAY: @{$array_ref} \n"; # Isolate reference with braces,
                                 # then dereference with a @ symbol.
  
%copy_camel = %{$hash_ref}; # Isolate reference with braces, and then
                            # dereference with a % to create hash copy.
  
foreach $key (keys %copy_camel) {
   print "HASH VALUE: $copy_camel{$key} \n"; # Get key, then value.
}

This code produces:

SCALAR: Asimov
ARRAY: Foundation and Earth I, Robot Nightfall
HASH VALUE: Cleon
HASH VALUE: Trevize
HASH VALUE: R. Daneel Olivaw

Arrow Notation

If you work carefully through the above code you'll see how the references are isolated by curly braces. The variable symbols, $, @, and %, are then used to dereference the data to the appropriate variable type. If this notation looks a little clumsy, relax, because you're among friends. For hashes and arrays, the arrow operator may ride to the rescue. Let's rewrite that code for the two array types:

for $index (0..$#{$array_ref}) {            # Work out size of array
  
  print "ARRAY $index: $array_ref->[$index] \n"; # Drill down arrow
}
  
for $key (keys %{$hash_ref}) { # Work out original keys
  
   print "HASH $key: $hash_ref->{$key} \n"; # Drill down arrow
}

The arrow operator can make life easier, because it makes the diagrammatic arrows in Figure A-4 come alive directly within the code:

ARRAY 0: Foundation and Earth
ARRAY 1: I, Robot
ARRAY 2: Nightfall
HASH Emperor: Cleon
HASH CouncilMan: Trevize
HASH Robot: R. Daneel Olivaw

The ref Operator

References are simply ordinary scalars, meaning that they can be stored in both arrays and hashes. We illustrate this in Figure A-5, where from a single $binary_tree_root_ref scalar we spider through a binary tree, made up of anonymous hashes, to quickly find ROWID information.

Figure A-5. Binary tree built from hashes
figs/pdba_aa05.gif

In dynamic coding like this, however, there is a problem. We're often unaware of what variable types our references are pointing to, which is information required for accurate de-referencing. The solution is then to use the ref operator. This returns the type of variable being pointed to. The main values returned by ref are detailed in Table A-4.

Table A-4. The main return values of ref

Return value

Description

undef

ref was supplied with a non-reference scalar

SCALAR

Points to a scalar

ARRAY

Points to an array

HASH

Points to a hash

CODE

Points to a subroutine

REF

Points to another scalar reference

We can now use ref to navigate our way around any data structure held together by references, with blocks of code such as the following:

if (( ref($this_ref) eq "SCALAR" ) || ( ref($this_ref) eq "REF" )) {
  
   # This reference is either pointing to an ordinary scalar,
   # or a scalar reference.  Deal with accordingly...
  
} elsif ( ref($this_ref) eq "ARRAY" ) {
  
   # This reference is pointing to an array...
  
} elsif ( ref($this_ref) eq "HASH" ) {
  
   # Pointing to a hash...
  
} elsif ( not ref $this_ref ) {
  
   # $this_ref is an ordinary scalar...
}

Anonymous Arrays and Hashes

In addition to generating named arrays and hashes, we can also generate anonymous arrays and hashes in Perl. Think of an anonymous array as being like an amateur ice hockey team being created spontaneously by a group of friends on a visit to a local ice rink. There's simply no need for a team name. This ability to create unnamed arrays on the fly creates enormous dynamism within our code, and is especially good for the creation of multidimensional arrays (think of a series of wooden Russian dolls opening up to reveal more dolls inside).

Here are the basics of creating anonymous arrays:

  1. To create a reference to a named array, we could use the following code:

    @le_carre = ('Tinker', 'Tailor', 'Soldier', 'Spy');
    $array_ref = \@le_carre;
  2. To refer to an anonymous array, we can just cut out the middleman:

    $array_ref = ['Tinker', 'Tailor', 'Soldier', 'Spy'];

    Think of the leading [ square bracket as being a scalar array reference in disguise, with everything up to the ] square bracket being inside the anonymous array.

  3. We can now create multidimensional arrays, extending the basic idea to create an outer array and two inner ones:

    $chess_ref = [ ["Black King", "Black Queen"],
                    ["White Bishop", "White Knight", "White Rook"]
                 ];
  4. We now have a two-dimensional array accessible from the $chess_ref scalar reference. For instance, to access "White Rook" we'd use:

    print $chess_ref->[1]->[2]; # 2nd inner array, 3rd element.

    (Remember that array indexes start from zero.)

  5. To dig out "Black King", we'd use the following:

    print $chess_ref->[0]->[0]; # 1st inner array, 1st element.
  6. We can also use more than two dimensions. So if between Oracle projects you're a part-time professor of astrophysics, specializing in 11-dimensional M-theory, Perl is the language for you.

You can create anonymous hashes in the same basic way:

  1. This time we use curly braces:

    $planets_ref = { Mercury => 
                        { Temp => 'Ridiculously Hot', Position => 1 },
                     Mars => 
                        { Temp => 'Blisteringly Cold', Position => 4 } };
  2. To get the position of Mars, relative to the sun, we would use:

    print $planets_ref->{Mars}->{Position}; # Key Mars, then Key Position.
  3. The temperature of Mercury would be:

    print $planets_ref->{Mercury}->{Temp}; # Key Mercury, then Key Temp

You can also mix and match your anonymous hash and array elements.

  1. Take a look at the following devil's advocate example:

    $stars_ref = { Aldebaran => 
                   [ { LightYears => 60 },
                     { Constellation => 
                       [ 'Taurus', 'Hyades', 'Crab Nebula' ] } ] } ;
  2. We want to get the third element of interest, under the Constellation flag, for the star Aldebaran, home of the Emperor Zurg. Can we find it?:

    print $stars_ref->{Aldebaran}->[1]->{Constellation}->[2];
  3. In the words of Buzz Lightyear, "yes, we can!"

    Crab Nebula
    Team LiB   Previous Section   Next Section