Recall that arrays and hashes contain only scalars; they cannot directly contain another array or hash as such. But considering that references can refer to an array or a hash and that references are scalars, you can see how one or more elements in an array or hash can point to other arrays or hashes. In this section, we will study how to build nested, heterogeneous data structures.
Let us say we would like to track a person’s details and that of their dependents. One approach is to create separate named hash tables for each person:
%sue = ( # Parent 'name' => 'Sue', 'age' => '45'); %john = ( # Child 'name' => 'John', 'age' => '20'); %peggy = ( # Child 'name' => 'Peggy', 'age' => '16');
The structures for John and Peggy can now be related to Sue like this:
@children = (\%john, \%peggy); $sue{'children'} = \@children; # Or $sue{'children'} = [\%john, \%peggy];
Figure 1.2 shows this structure after it has been built.
This is how you can print Peggy’s age, given
%sue
:
print $sue{children}->[1]->{age};
Suppose the first line in your program is this:
$sue{children}->[1]->{age} = 10;
Perl automatically creates the hash %sue
, gives it
a hash element indexed by the string children
,
points that entry to a newly allocated array, whose second element is
made to refer to a freshly allocated hash, which gets an entry
indexed by the string age
. Talk about programmer
efficiency.
While on the subject of programmer
efficiency, let us discuss one more optimization for typing. You can
omit ->
if (and only if) it is between
subscripts. That is, the following expressions are equivalent:
print $sue{children}->[1]->{age}; print $sue{children}[1]{age};
This is similar to the way C implements multidimensional arrays, in which every index except the last one behaves like a pointer to the next level (or dimension) and the final index corresponds to the actual data. The difference — which doesn’t really matter at a usage level — between C’s and Perl’s approaches is that C treats an n-dimensional array as a contiguous stream of bytes and does not allocate space for pointers to subarrays, whereas Perl allocates space for references to intermediate single-dimension arrays.
Continuing from where we left off, you will find that even such a simple example benefits from using anonymous arrays and hashes, rather than named ones, as shown in the following snippet:
%sue = ( # Parent 'name' => 'Sue', 'age' => '45', 'children' => [ # Anon array of two hashes { # Anon hash 1 'name' => 'John', 'age' => '20' }, { # Anon hash 2 'name' => 'Peggy', 'age' => '16' } ] );
This snippet of code contains only one named variable. The
“children” attribute is a reference to an anonymous
array, which itself contains references to anonymous hashes
containing the children’s details. This nesting can be as deep
as you want; for example, you might represent John’s
educational qualifications as a reference to an anonymous array of
hash records (each of which contain details of school attended, grade
points, and so on). None of these arrays or hashes actually embed the
next level hash or array; recall that the anonymous array and hash
syntax yields references, which is what the containing structures
see. In other words, such a nesting does not reflect a
containment hierarchy. Try print values(%sue)
to convince yourself.
It is comforting to know that Perl automatically deletes all nested
structures as soon as the top-level structure
(%sue
) is deleted or reassigned to something else.
Internal structures or elements that are are still referred to
elsewhere aren’t deleted.
Get Advanced Perl Programming now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.