More regexp documentation
[p5sagit/p5-mst-13.2.git] / pod / perldsc.pod
index c7c1be2..fd6403b 100644 (file)
@@ -1,4 +1,5 @@
 =head1 NAME
+X<data structure> X<complex data structure> X<struct>
 
 perldsc - Perl Data Structures Cookbook
 
@@ -15,14 +16,14 @@ hacked Perl's internal symbol table directly, a strategy that proved hard
 to develop and maintain--to put it mildly.
 
 The 5.0 release of Perl let us have complex data structures.  You
-may now write something like this and all of a sudden, you'd have a array
+may now write something like this and all of a sudden, you'd have an array
 with three dimensions!
 
-    my @AoA;
-    for my $x (1 .. 10) {
-       for my $y (1 .. 10) {
-           for my $z (1 .. 10) {
-               $AoA[$x][$y][$z] = $x ** $y + $z;
+    for $x (1 .. 10) {
+       for $y (1 .. 10) {
+           for $z (1 .. 10) {
+               $AoA[$x][$y][$z] =
+                   $x ** $y + $z;
            }
        }
     }
@@ -32,7 +33,7 @@ elaborate construct than meets the eye!
 
 How do you print it out?  Why can't you say just C<print @AoA>?  How do
 you sort it?  How can you pass it to a function or get one of these back
-from a function?  Is is an object?  Can you save it to disk to read
+from a function?  Is it an object?  Can you save it to disk to read
 back later?  How do you access whole rows or columns of that matrix?  Do
 all the values have to be numeric?
 
@@ -68,6 +69,7 @@ But for now, let's look at general issues common to all
 these types of data structures.
 
 =head1 REFERENCES
+X<reference> X<dereference> X<dereferencing> X<pointer>
 
 The most important thing to understand about all data structures in Perl
 -- including multidimensional arrays--is that even though they might
@@ -75,8 +77,9 @@ appear otherwise, Perl C<@ARRAY>s and C<%HASH>es are all internally
 one-dimensional.  They can hold only scalar values (meaning a string,
 number, or a reference).  They cannot directly contain other arrays or
 hashes, but instead contain I<references> to other arrays or hashes.
+X<multidimensional array> X<array, multidimensional>
 
-You can't use a reference to a array or hash in quite the same way that you
+You can't use a reference to an array or hash in quite the same way that you
 would a real array or hash.  For C or C++ programmers unused to
 distinguishing between arrays and pointers to the same, this can be
 confusing.  If so, just think of it as the difference between a structure
@@ -102,11 +105,7 @@ Now, because the top level contains only references, if you try to print
 out your array in with a simple print() function, you'll get something
 that doesn't look very nice, like this:
 
-    my @AoA = (
-               [2, 3,  ],
-               [4, 5, 7],
-               [0,     ],
-              );
+    @AoA = ( [2, 3], [4, 5, 7], [0] );
     print $AoA[1][2];
   7
     print @AoA;
@@ -127,46 +126,80 @@ elements or else taking a reference to the same memory location
 repeatedly.  Here's the case where you just get the count instead
 of a nested array:
 
-    my @AoA;
-    for my $i (1..10) {
-       my @array   = somefunc($i);
-          $AoA[$i] = @array;       # WRONG!
+    for $i (1..10) {
+       @array = somefunc($i);
+       $AoA[$i] = @array;      # WRONG!
     }
 
 That's just the simple case of assigning an array to a scalar and getting
 its element count.  If that's what you really and truly want, then you
 might do well to consider being a tad more explicit about it, like this:
 
-    my @counts;
-    for my $i (1..10) {
-       my @array      = somefunc($i);
-          $counts[$i] = scalar @array;
+    for $i (1..10) {
+       @array = somefunc($i);
+       $counts[$i] = scalar @array;
     }
 
-Here's the right way to do the reference C<@array>:
+Here's the case of taking a reference to the same memory location
+again and again:
 
-    my @AoA
-    for my $i (1..10) {
-       my @array   = somefunc($i);
-          $AoA[$i] = [ @array ];
+    for $i (1..10) {
+       @array = somefunc($i);
+       $AoA[$i] = \@array;     # WRONG!
+    }
+
+So, what's the big problem with that?  It looks right, doesn't it?
+After all, I just told you that you need an array of references, so by
+golly, you've made me one!
+
+Unfortunately, while this is true, it's still broken.  All the references
+in @AoA refer to the I<very same place>, and they will therefore all hold
+whatever was last in @array!  It's similar to the problem demonstrated in
+the following C program:
+
+    #include <pwd.h>
+    main() {
+       struct passwd *getpwnam(), *rp, *dp;
+       rp = getpwnam("root");
+       dp = getpwnam("daemon");
+
+       printf("daemon name is %s\nroot name is %s\n",
+               dp->pw_name, rp->pw_name);
+    }
+
+Which will print
+
+    daemon name is daemon
+    root name is daemon
+
+The problem is that both C<rp> and C<dp> are pointers to the same location
+in memory!  In C, you'd have to remember to malloc() yourself some new
+memory.  In Perl, you'll want to use the array constructor C<[]> or the
+hash constructor C<{}> instead.   Here's the right way to do the preceding
+broken code fragments:
+X<[]> X<{}>
+
+    for $i (1..10) {
+       @array = somefunc($i);
+       $AoA[$i] = [ @array ];
     }
 
 The square brackets make a reference to a new array with a I<copy>
-of what's in C<@array>.
+of what's in @array at the time of the assignment.  This is what
+you want.
 
 Note that this will produce something similar, but it's
 much harder to read:
 
-    my @AoA;
-    for my $i (1..10) {
-       my @array        = somefunc($i);
-          @{ $AoA[$i] } = @array;
+    for $i (1..10) {
+       @array = 0 .. $i;
+       @{$AoA[$i]} = @array;
     }
 
 Is it the same?  Well, maybe so--and maybe not.  The subtle difference
 is that when you assign something in square brackets, you know for sure
 it's always a brand new reference with a new I<copy> of the data.
-Something else could be going on in this new case with the C<@{ $AoA[$i]} }>
+Something else could be going on in this new case with the C<@{$AoA[$i]}>
 dereference on the left-hand-side of the assignment.  It all depends on
 whether C<$AoA[$i]> had been undefined to start with, or whether it
 already contained a reference.  If you had already populated @AoA with
@@ -177,7 +210,7 @@ references, as in
 Then the assignment with the indirection on the left-hand-side would
 use the existing reference that was already there:
 
-    @{ $AoA[3] } = @array;
+    @{$AoA[3]} = @array;
 
 Of course, this I<would> have the "interesting" effect of clobbering
 @another_array.  (Have you ever noticed how when a programmer says
@@ -192,10 +225,9 @@ efficient.
 Surprisingly, the following dangerous-looking construct will
 actually work out fine:
 
-    my @AoA;
-    for my $i (1..10) {
-        my @array   = somefunc($i);
-           $AoA[$i] = \@array;
+    for $i (1..10) {
+        my @array = somefunc($i);
+        $AoA[$i] = \@array;
     }
 
 That's because my() is more of a run-time statement than it is a
@@ -214,15 +246,17 @@ do the right thing behind the scenes.
 
 In summary:
 
-    $AoA[$i]      = [ @array ];        # usually best
-    $AoA[$i]      =  \@array;  # perilous; just how my() is that array?
-    @{ $AoA[$i] } =   @array;  # way too tricky for most programmers
+    $AoA[$i] = [ @array ];     # usually best
+    $AoA[$i] = \@array;                # perilous; just how my() was that array?
+    @{ $AoA[$i] } = @array;    # way too tricky for most programmers
 
 
 =head1 CAVEAT ON PRECEDENCE
+X<dereference, precedence> X<dereferencing, precedence>
 
-Speaking of things like C<@{ $AoA[$i] }>, the following are actually the
+Speaking of things like C<@{$AoA[$i]}>, the following are actually the
 same thing:
+X<< -> >>
 
     $aref->[2][2]      # clear
     $$aref[2][2]       # confusing
@@ -256,9 +290,9 @@ also disallow accidental "symbolic dereferencing".  Therefore if you'd done
 this:
 
     my $aref = [
-       [ 'fred',   'barney', 'pebbles', 'bambam', 'dino', ],
-       [ 'homer',  'bart',   'marge',   'maggie',         ],
-       [ 'george', 'jane',   'elroy',   'judy',           ],
+       [ "fred", "barney", "pebbles", "bambam", "dino", ],
+       [ "homer", "bart", "marge", "maggie", ],
+       [ "george", "jane", "elroy", "judy", ],
     ];
 
     print $aref[2][2];
@@ -270,6 +304,10 @@ variable, and it would thereby remind you to write instead:
     print $aref->[2][2]
 
 =head1 DEBUGGING
+X<data structure, debugging> X<complex data structure, debugging>
+X<AoA, debugging> X<HoA, debugging> X<AoH, debugging> X<HoH, debugging>
+X<array of arrays, debugging> X<hash of arrays, debugging>
+X<array of hashes, debugging> X<hash of hashes, debugging>
 
 Before version 5.002, the standard Perl debugger didn't do a very nice job of
 printing out complex data structures.  With 5.002 or above, the
@@ -303,184 +341,172 @@ here are short code examples illustrating access of various
 types of data structures.
 
 =head1 ARRAYS OF ARRAYS
+X<array of arrays> X<AoA>
 
-=head2 Declaration of a ARRAY OF ARRAYS
+=head2 Declaration of an ARRAY OF ARRAYS
 
- my @AoA = (
-        [ 'fred',   'barney'         ],
-        [ 'george', 'jane',  'elroy' ],
-        [ 'homer',  'marge', 'bart'  ],
+ @AoA = (
+        [ "fred", "barney" ],
+        [ "george", "jane", "elroy" ],
+        [ "homer", "marge", "bart" ],
       );
 
-=head2 Generation of a ARRAY OF ARRAYS
+=head2 Generation of an ARRAY OF ARRAYS
 
  # reading from file
- my @AoA;
  while ( <> ) {
      push @AoA, [ split ];
  }
 
  # calling a function
- my @AoA;
- foreach my $i ( 1 .. 10 ) {
+ for $i ( 1 .. 10 ) {
      $AoA[$i] = [ somefunc($i) ];
  }
 
  # using temp vars
- my @AoA;
- foreach my $i ( 1 .. 10 ) {
-     my @tmp     = somefunc($i);
-        $AoA[$i] = [ @tmp ];
+ for $i ( 1 .. 10 ) {
+     @tmp = somefunc($i);
+     $AoA[$i] = [ @tmp ];
  }
 
  # add to an existing row
- push @{ $AoA[0] }, 'wilma', 'betty';
-
-=head2 Access and Printing of a ARRAY OF ARRAYS
+ push @{ $AoA[0] }, "wilma", "betty";
 
- my @AoA;
+=head2 Access and Printing of an ARRAY OF ARRAYS
 
  # one element
- $AoA[0][0] = 'Fred';
+ $AoA[0][0] = "Fred";
 
  # another element
  $AoA[1][1] =~ s/(\w)/\u$1/;
 
  # print the whole thing with refs
- foreach my $aref ( @AoA ) {
+ for $aref ( @AoA ) {
      print "\t [ @$aref ],\n";
  }
 
  # print the whole thing with indices
- foreach my $i ( 0 .. $#AoA ) {
-     print "\t [ @{ $AoA[$i] } ],\n";
+ for $i ( 0 .. $#AoA ) {
+     print "\t [ @{$AoA[$i]} ],\n";
  }
 
  # print the whole thing one at a time
- foreach my $i ( 0 .. $#AoA ) {
-     foreach my $j ( 0 .. $#{ $AoA[$i] } ) {
-         print "element $i $j is $AoA[$i][$j]\n";
+ for $i ( 0 .. $#AoA ) {
+     for $j ( 0 .. $#{ $AoA[$i] } ) {
+         print "elt $i $j is $AoA[$i][$j]\n";
      }
  }
 
 =head1 HASHES OF ARRAYS
+X<hash of arrays> X<HoA>
 
 =head2 Declaration of a HASH OF ARRAYS
 
- my %HoA = (
-        flintstones => [ 'fred',   'barney'         ],
-        jetsons     => [ 'george', 'jane',  'elroy' ],
-        simpsons    => [ 'homer',  'marge', 'bart'  ],
+ %HoA = (
+        flintstones        => [ "fred", "barney" ],
+        jetsons            => [ "george", "jane", "elroy" ],
+        simpsons           => [ "homer", "marge", "bart" ],
       );
 
 =head2 Generation of a HASH OF ARRAYS
 
  # reading from file
  # flintstones: fred barney wilma dino
- my %HoA;
  while ( <> ) {
-     next unless s/^([^:]*):\s*//;
+     next unless s/^(.*?):\s*//;
      $HoA{$1} = [ split ];
  }
 
  # reading from file; more temps
  # flintstones: fred barney wilma dino
- my %HoA;
- while ( my $line = <> ) {
-     my ($who, $rest) = split /:\s*/, $line, 2;
-     my @fields       = split ' ', $rest;
-        $HoA{$who}    = [ @fields ];
+ while ( $line = <> ) {
+     ($who, $rest) = split /:\s*/, $line, 2;
+     @fields = split ' ', $rest;
+     $HoA{$who} = [ @fields ];
  }
 
  # calling a function that returns a list
- my %HoA;
- foreach my $group ( 'simpsons', 'jetsons', 'flintstones' ) {
+ for $group ( "simpsons", "jetsons", "flintstones" ) {
      $HoA{$group} = [ get_family($group) ];
  }
 
  # likewise, but using temps
- my %HoA;
- foreach my $group ( 'simpsons', 'jetsons', 'flintstones' ) {
-     my @members     = get_family($group);
-        $HoA{$group} = [ @members ];
+ for $group ( "simpsons", "jetsons", "flintstones" ) {
+     @members = get_family($group);
+     $HoA{$group} = [ @members ];
  }
 
  # append new members to an existing family
- push @{ $HoA{flintstones} }, 'wilma', 'betty';
+ push @{ $HoA{"flintstones"} }, "wilma", "betty";
 
 =head2 Access and Printing of a HASH OF ARRAYS
 
- my %HoA;
-
  # one element
- $HoA{flintstones}[0] = 'Fred';
+ $HoA{flintstones}[0] = "Fred";
 
  # another element
  $HoA{simpsons}[1] =~ s/(\w)/\u$1/;
 
  # print the whole thing
- foreach my $family ( keys %HoA ) {
-     print "$family: @{ $HoA{$family} }\n";
+ foreach $family ( keys %HoA ) {
+     print "$family: @{ $HoA{$family} }\n"
  }
 
  # print the whole thing with indices
- foreach my $family ( keys %HoA ) {
-     print 'family: ';
-     foreach my $i ( 0 .. $#{ $HoA{$family} } ) {
+ foreach $family ( keys %HoA ) {
+     print "family: ";
+     foreach $i ( 0 .. $#{ $HoA{$family} } ) {
          print " $i = $HoA{$family}[$i]";
      }
      print "\n";
  }
 
  # print the whole thing sorted by number of members
- sub num_members {
-   @{ $HoA{$b} } <=> @{ $HoA{$a} }
- }
- foreach my $family ( sort num_members keys %HoA ) {
+ foreach $family ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ) {
      print "$family: @{ $HoA{$family} }\n"
  }
 
  # print the whole thing sorted by number of members and name
- sub members_and_name {
-   @{ $HoA{$b} } <=> @{ $HoA{$a} }
-                ||
-             $a cmp $b
- }
- foreach my $family ( sort members_and_name keys %HoA ) {
+ foreach $family ( sort {
+                           @{$HoA{$b}} <=> @{$HoA{$a}}
+                                       ||
+                                   $a cmp $b
+           } keys %HoA )
+ {
      print "$family: ", join(", ", sort @{ $HoA{$family} }), "\n";
  }
 
 =head1 ARRAYS OF HASHES
+X<array of hashes> X<AoH>
 
-=head2 Declaration of a ARRAY OF HASHES
+=head2 Declaration of an ARRAY OF HASHES
 
- my @AoH = (
+ @AoH = (
         {
-            Lead     => 'fred',
-            Friend   => 'barney',
+            Lead     => "fred",
+            Friend   => "barney",
         },
         {
-            Lead     => 'george',
-            Wife     => 'jane',
-            Son      => 'elroy',
+            Lead     => "george",
+            Wife     => "jane",
+            Son      => "elroy",
         },
         {
-            Lead     => 'homer',
-            Wife     => 'marge',
-            Son      => 'bart',
+            Lead     => "homer",
+            Wife     => "marge",
+            Son      => "bart",
         }
   );
 
-=head2 Generation of a ARRAY OF HASHES
+=head2 Generation of an ARRAY OF HASHES
 
  # reading from file
  # format: LEAD=fred FRIEND=barney
- my @AoH;
  while ( <> ) {
-     my $rec = {};
-     foreach my $field ( split ) {
-         my($key, $value) = split /=/, $field;
-         $rec->{$key}     = $value;
+     $rec = {};
+     for $field ( split ) {
+         ($key, $value) = split /=/, $field;
+         $rec->{$key} = $value;
      }
      push @AoH, $rec;
  }
@@ -489,81 +515,77 @@ types of data structures.
  # reading from file
  # format: LEAD=fred FRIEND=barney
  # no temp
- my @AoH;
  while ( <> ) {
      push @AoH, { split /[\s+=]/ };
  }
 
  # calling a function  that returns a key/value pair list, like
- # lead => 'fred', daughter => 'pebbles'
- my @AoH;
- while ( my %fields = getnextpairset() ) {
+ # "lead","fred","daughter","pebbles"
+ while ( %fields = getnextpairset() ) {
      push @AoH, { %fields };
  }
 
  # likewise, but using no temp vars
- my @AoH;
  while (<>) {
      push @AoH, { parsepairs($_) };
  }
 
  # add key/value to an element
- $AoH[0]{pet} = 'dino';
+ $AoH[0]{pet} = "dino";
  $AoH[2]{pet} = "santa's little helper";
 
-=head2 Access and Printing of a ARRAY OF HASHES
-
- my @AoH;
+=head2 Access and Printing of an ARRAY OF HASHES
 
  # one element
- $AoH[0]{lead} = 'fred';
+ $AoH[0]{lead} = "fred";
 
  # another element
  $AoH[1]{lead} =~ s/(\w)/\u$1/;
 
  # print the whole thing with refs
- foreach my $href ( @AoH ) {
-     print '{ ';
-     foreach my $role ( keys %$href ) {
-         print "$role = $href->{$role} ";
+ for $href ( @AoH ) {
+     print "{ ";
+     for $role ( keys %$href ) {
+         print "$role=$href->{$role} ";
      }
      print "}\n";
  }
 
  # print the whole thing with indices
- foreach my $i ( 0 .. $#AoH ) {
+ for $i ( 0 .. $#AoH ) {
      print "$i is { ";
-     foreach my $role ( keys %{ $AoH[$i] } ) {
-         print "$role = $AoH[$i]{$role} ";
+     for $role ( keys %{ $AoH[$i] } ) {
+         print "$role=$AoH[$i]{$role} ";
      }
      print "}\n";
  }
 
  # print the whole thing one at a time
- foreach my $i ( 0 .. $#AoH ) {
-     foreach my $role ( keys %{ $AoH[$i] } ) {
-         print "element $i $role is $AoH[$i]{$role}\n";
+ for $i ( 0 .. $#AoH ) {
+     for $role ( keys %{ $AoH[$i] } ) {
+         print "elt $i $role is $AoH[$i]{$role}\n";
      }
  }
 
 =head1 HASHES OF HASHES
+X<hass of hashes> X<HoH>
 
 =head2 Declaration of a HASH OF HASHES
 
- my %HoH = (
+ %HoH = (
         flintstones => {
-               lead      => 'fred',
-               pal       => 'barney',
+               lead      => "fred",
+               pal       => "barney",
         },
         jetsons     => {
-               lead      => 'george',
-               wife      => 'jane',
-               'his boy' => 'elroy',
+               lead      => "george",
+               wife      => "jane",
+               "his boy" => "elroy",
         },
         simpsons    => {
-               lead      => 'homer',
-               wife      => 'marge',
-               kid       => 'bart',
+               lead      => "homer",
+               wife      => "marge",
+               kid       => "bart",
        },
  );
 
@@ -571,126 +593,108 @@ types of data structures.
 
  # reading from file
  # flintstones: lead=fred pal=barney wife=wilma pet=dino
- my %HoH;
  while ( <> ) {
-     next unless s/^([^:]*):\s*//;
-     my $who = $1;
-     for my $field ( split ) {
-         my($key, $value) = split /=/, $field;
+     next unless s/^(.*?):\s*//;
+     $who = $1;
+     for $field ( split ) {
+         ($key, $value) = split /=/, $field;
          $HoH{$who}{$key} = $value;
      }
 
 
  # reading from file; more temps
- my %HoH;
  while ( <> ) {
-     next unless s/^([^:]*):\s*//;
-     my $who = $1;
-     my $rec = {};
+     next unless s/^(.*?):\s*//;
+     $who = $1;
+     $rec = {};
      $HoH{$who} = $rec;
-     foreach my $field ( split ) {
-         my($key, $value) = split /=/, $field;
-         $rec->{$key}     = $value;
+     for $field ( split ) {
+         ($key, $value) = split /=/, $field;
+         $rec->{$key} = $value;
      }
  }
 
  # calling a function  that returns a key,value hash
- my %HoH;
- foreach my $group ( 'simpsons', 'jetsons', 'flintstones' ) {
+ for $group ( "simpsons", "jetsons", "flintstones" ) {
      $HoH{$group} = { get_family($group) };
  }
 
  # likewise, but using temps
- my %HoH;
- foreach my $group ( 'simpsons', 'jetsons', 'flintstones' ) {
-     my %members  = get_family($group);
+ for $group ( "simpsons", "jetsons", "flintstones" ) {
+     %members = get_family($group);
      $HoH{$group} = { %members };
  }
 
  # append new members to an existing family
- my %HoH;
- my %new_folks = (
-     wife => 'wilma',
-     pet  => 'dino',
+ %new_folks = (
+     wife => "wilma",
+     pet  => "dino",
  );
 
- foreach my $what (keys %new_folks) {
+ for $what (keys %new_folks) {
      $HoH{flintstones}{$what} = $new_folks{$what};
  }
 
 =head2 Access and Printing of a HASH OF HASHES
 
- %HoH;
-
  # one element
- $HoH{flintstones}{wife} = 'wilma';
+ $HoH{flintstones}{wife} = "wilma";
 
  # another element
  $HoH{simpsons}{lead} =~ s/(\w)/\u$1/;
 
  # print the whole thing
- foreach my $family ( keys %HoH ) {
+ foreach $family ( keys %HoH ) {
      print "$family: { ";
-     foreach my $role ( keys %{ $HoH{$family} } ) {
-         print "$role = $HoH{$family}{$role} ";
+     for $role ( keys %{ $HoH{$family} } ) {
+         print "$role=$HoH{$family}{$role} ";
      }
      print "}\n";
  }
 
  # print the whole thing  somewhat sorted
- foreach my $family ( sort keys %HoH ) {
+ foreach $family ( sort keys %HoH ) {
      print "$family: { ";
-     foreach my $role ( sort keys %{ $HoH{$family} } ) {
-         print "$role = $HoH{$family}{$role} ";
+     for $role ( sort keys %{ $HoH{$family} } ) {
+         print "$role=$HoH{$family}{$role} ";
      }
      print "}\n";
  }
 
+
  # print the whole thing sorted by number of members
- sub num_members {
-   keys %{ $HoH{$b} }  <=>  keys %{ $HoH{$a} }
- }
- foreach my $family ( sort num_members keys %HoH ) {
+ foreach $family ( sort { keys %{$HoH{$b}} <=> keys %{$HoH{$a}} } keys %HoH ) {
      print "$family: { ";
-     foreach my $role ( sort keys %{ $HoH{$family} } ) {
-         print "$role = $HoH{$family}{$role} ";
+     for $role ( sort keys %{ $HoH{$family} } ) {
+         print "$role=$HoH{$family}{$role} ";
      }
      print "}\n";
  }
 
  # establish a sort order (rank) for each role
- my %rank;
- my $i = 0;
- foreach ( qw(lead wife son daughter pal pet) ) {
-   $rank{$_} = ++$i;
- }
+ $i = 0;
+ for ( qw(lead wife son daughter pal pet) ) { $rank{$_} = ++$i }
 
  # now print the whole thing sorted by number of members
- sub num_members {
-   keys %{ $HoH{$b} }  <=>  keys %{ $HoH{$a} }
- }
- sub rank {
-   $rank{$a} <=> $rank{$b}
- }
-
- foreach my $family ( sort num_members keys %HoH ) {
+ foreach $family ( sort { keys %{ $HoH{$b} } <=> keys %{ $HoH{$a} } } keys %HoH ) {
      print "$family: { ";
      # and print these according to rank order
-     foreach my $role ( sort rank keys %{ $HoH{$family} } ) {
-         print "$role = $HoH{$family}{$role} ";
+     for $role ( sort { $rank{$a} <=> $rank{$b} }  keys %{ $HoH{$family} } ) {
+         print "$role=$HoH{$family}{$role} ";
      }
      print "}\n";
  }
 
 
 =head1 MORE ELABORATE RECORDS
+X<record> X<structure> X<struct>
 
 =head2 Declaration of MORE ELABORATE RECORDS
 
 Here's a sample showing how to create and use a record whose fields are of
 many different sorts:
 
-     my $rec = {
+     $rec = {
         TEXT      => $string,
         SEQUENCE  => [ @old_values ],
         LOOKUP    => { %some_table },
@@ -701,14 +705,14 @@ many different sorts:
 
      print $rec->{TEXT};
 
-     print $rec->{SEQUENCE}->[0];
-     my $last = pop @{ $rec->{SEQUENCE} };
+     print $rec->{SEQUENCE}[0];
+     $last = pop @ { $rec->{SEQUENCE} };
 
-     print $rec->{LOOKUP}->{key};
-     my($first_k, $first_v) = each %{ $rec->{LOOKUP} };
+     print $rec->{LOOKUP}{"key"};
+     ($first_k, $first_v) = each %{ $rec->{LOOKUP} };
 
-     my $answer = $rec->{THATCODE}->($arg);
-     my $result = $rec->{THISCODE}->($arg1, $arg2);
+     $answer = $rec->{THATCODE}->($arg);
+     $answer = $rec->{THISCODE}->($arg1, $arg2);
 
      # careful of extra block braces on fh ref
      print { $rec->{HANDLE} } "a string\n";
@@ -719,52 +723,55 @@ many different sorts:
 
 =head2 Declaration of a HASH OF COMPLEX RECORDS
 
-     my %TV = (
+     %TV = (
         flintstones => {
-            series   => 'flintstones',
+            series   => "flintstones",
             nights   => [ qw(monday thursday friday) ],
             members  => [
-                { name => 'fred',    role => 'lead', age  => 36, },
-                { name => 'wilma',   role => 'wife', age  => 31, },
-                { name => 'pebbles', role => 'kid',  age  =>  4, },
+                { name => "fred",    role => "lead", age  => 36, },
+                { name => "wilma",   role => "wife", age  => 31, },
+                { name => "pebbles", role => "kid",  age  =>  4, },
             ],
         },
 
         jetsons     => {
-            series   => 'jetsons',
+            series   => "jetsons",
             nights   => [ qw(wednesday saturday) ],
             members  => [
-                { name => 'george",  role => 'lead', age  => 41, },
-                { name => 'jane",    role => 'wife', age  => 39, },
-                { name => 'elroy",   role => 'kid',  age  =>  9, },
+                { name => "george",  role => "lead", age  => 41, },
+                { name => "jane",    role => "wife", age  => 39, },
+                { name => "elroy",   role => "kid",  age  =>  9, },
             ],
          },
 
         simpsons    => {
-            series   => 'simpsons',
+            series   => "simpsons",
             nights   => [ qw(monday) ],
             members  => [
-                { name => 'homer', role => 'lead', age => 34, },
-                { name => 'marge', role => 'wife', age => 37, },
-                { name => 'bart',  role => 'kid',  age => 11, },
+                { name => "homer", role => "lead", age  => 34, },
+                { name => "marge", role => "wife", age => 37, },
+                { name => "bart",  role => "kid",  age  =>  11, },
             ],
          },
       );
 
 =head2 Generation of a HASH OF COMPLEX RECORDS
 
-Here's a piece by piece build up of a hash of complex records.  We'll
-read in a file that has our data in it.
+     # reading from file
+     # this is most easily done by having the file itself be
+     # in the raw data format as shown above.  perl is happy
+     # to parse complex data structures if declared as data, so
+     # sometimes it's easiest to do that
 
-     my %TV  = ();
-     my $rec = {};
-     $rec->{series} = 'flintstones';
+     # here's a piece by piece build up
+     $rec = {};
+     $rec->{series} = "flintstones";
      $rec->{nights} = [ find_days() ];
 
-     my @members = ();
+     @members = ();
      # assume this file in field=value syntax
-     while ( <> ) {
-         my %fields = split /[\s=]+/, $_;
+     while (<>) {
+         %fields = split /[\s=]+/;
          push @members, { %fields };
      }
      $rec->{members} = [ @members ];
@@ -772,18 +779,19 @@ read in a file that has our data in it.
      # now remember the whole thing
      $TV{ $rec->{series} } = $rec;
 
-Now, you might want to make interesting extra fields that
-include pointers back into the same data structure so if
-change one piece, it changes everywhere, like for example
-if you wanted a 'kids' field that was a reference
-to an array of the kids' records without having duplicate
-records and thus update problems.
-
-     foreach my $family ( keys %TV ) {
-         my $rec  = $TV{$family}; # $rec points to $TV{$family}
-         my @kids = ();
-         foreach my $person ( @{ $rec->{members} } ) {
-             if ( $person->{role} =~ /kid|son|daughter/ ) {
+     ###########################################################
+     # now, you might want to make interesting extra fields that
+     # include pointers back into the same data structure so if
+     # change one piece, it changes everywhere, like for example
+     # if you wanted a {kids} field that was a reference
+     # to an array of the kids' records without having duplicate
+     # records and thus update problems.
+     ###########################################################
+     foreach $family (keys %TV) {
+         $rec = $TV{$family}; # temp pointer
+         @kids = ();
+         for $person ( @{ $rec->{members} } ) {
+             if ($person->{role} =~ /kid|son|daughter/) {
                  push @kids, $person;
              }
          }
@@ -791,33 +799,30 @@ records and thus update problems.
          $rec->{kids} = [ @kids ];
      }
 
-You copied the array, but the array itself contains pointers
-to uncopied objects. This means that if you make bart get
-older via
+     # you copied the array, but the array itself contains pointers
+     # to uncopied objects. this means that if you make bart get
+     # older via
 
      $TV{simpsons}{kids}[0]{age}++;
 
-Then this would also change in C<$TV{simpsons}{members}[2]{age}>
-because C<$TV{simpsons}{kids}[0]> and C<$TV{simpsons}{members}[2]>
-both point to the same underlying anonymous hash table.
+     # then this would also change in
+     print $TV{simpsons}{members}[2]{age};
 
-     # print the whole thing
-     foreach my $family ( keys %TV ) {
-         print "the $family is on during @{ $TV{$family}{nights} }\n",
-               "its members are:\n";
+     # because $TV{simpsons}{kids}[0] and $TV{simpsons}{members}[2]
+     # both point to the same underlying anonymous hash table
 
-         foraech my $who ( @{ $TV{$family}{members} } ) {
+     # print the whole thing
+     foreach $family ( keys %TV ) {
+         print "the $family";
+         print " is on during @{ $TV{$family}{nights} }\n";
+         print "its members are:\n";
+         for $who ( @{ $TV{$family}{members} } ) {
              print " $who->{name} ($who->{role}), age $who->{age}\n";
          }
-
-         print "it turns out that $TV{$family}{lead} has ",
-               scalar ( @{ $TV{$family}{kids} } ),
-               ' kids named ',
-               join(
-                    ', ',
-                    map { $_->{name} } @{ $TV{$family}{kids} }
-                   ),
-               "\n";
+         print "it turns out that $TV{$family}{lead} has ";
+         print scalar ( @{ $TV{$family}{kids} } ), " kids named ";
+         print join (", ", map { $_->{name} } @{ $TV{$family}{kids} } );
+         print "\n";
      }
 
 =head1 Database Ties
@@ -838,8 +843,5 @@ perlref(1), perllol(1), perldata(1), perlobj(1)
 
 Tom Christiansen <F<tchrist@perl.com>>
 
-Last update (by Tom):
+Last update:
 Wed Oct 23 04:57:50 MET DST 1996
-
-Last update (by Casey West, <F<casey@geeknest.com>>
-Mon Sep 17 13:33:41 EDT 2001