release 0.1 (untested, may have typos)
Sunday, 1 October 1995
This is a cookbook of recipes for building up complex data structures in perl5. It has been extracted from a much larger and more expository document to be published in pod format and included with the standard perl distribution. The goal is to provide cookbook-like, cut-and-paste examples of the most often used data structures in perl. Think of the recipes as a quick reference
It currently has 6 parts:
The first thing you need to do is figure out how you want to access (such as via an assignment) just one individual element of your data structure just using lists and hashes. Use a list if you're thinking of an array, use a hash if you're thinking of a record or a lookup table.
Here are some further tips of general interest:
push @{ $a[3] }, @new_listpush $a[3], @new_list- foreach $k (keys %{ $h{"key"} }) { ... }
- foreach $k (keys $h{"key"}) { ... }
while ( <> ) {
@fields = split;
push @a, [ @fields ];
}
This generally means never using the backslash to take
a reference, but rather using the [] or {} constructors.
This, for examples, is wrong!
while ( <> ) {
@fields = split;
push @a, \@fields;
}
An exception to this rule would be when you're writing a recursive data structure or are creating multiple key indices for the same set of records.
- @ { $a[$i] } = @list
- $a[$i] = [ @list ]
@LoL = (
[ "fred", "barney" ],
[ "george", "jane", "elroy" ],
[ "homer", "marge", "bart" ],
);
# reading from file
while ( <> ) {
push @LoL, [ split ];
}
# calling a function
for $i ( 1 .. 10 ) {
$LoL[$i] = [ somefunc($i) ];
}
# using temp vars
for $i ( 1 .. 10 ) {
@tmp = somefunc($i);
$LoL[$i] = [ @tmp ];
}
# add to an existing row
push @{ $LoL[0] }, "wilma", "betty";
# one element
$LoL[0][0] = "Fred";
# another element
$LoL[1][1] =~ s/(\w)/\u$1/;
# print the whole thing with refs
for $aref ( @LoL ) {
print "\t [ @$aref ],\n";
}
# print the whole thing with indices
for $i ( 0 .. $#LoL ) {
print "\t [ @{$LoL[$i]} ],\n";
}
# print the whole thing one at a time
for $i ( 0 .. $#LoL ) {
for $j ( 0 .. $#{$LoL[$i]} ) {
print "elt $i $j is $LoL[$i][$j]\n";
}
}
%HoL = (
"flintstones" => [ "fred", "barney" ],
"jetsons" => [ "george", "jane", "elroy" ],
"simpsons" => [ "homer", "marge", "bart" ],
);
# reading from file
# flintstones: fred barney wilma dino
while ( <> ) {
next unless s/^(.*?):\s*//;
$HoL{$1} = [ split ];
}
# reading from file; more temps
# flintstones: fred barney wilma dino
while ( $line = <> ) {
($who, $rest) = split /:\s*/, $line, 2;
@fields = split ' ', $rest;
$HoL{$who} = [ @fields ];
}
# calling a function that returns a list
for $group ( "simpsons", "jetsons", "flintstones" ) {
$HoL{$group} = [ get_family($group) ];
}
# likewise, but using temps
for $group ( "simpsons", "jetsons", "flintstones" ) {
@members = get_family($group);
$HoL{$group} = [ @members ];
}
# append new members to an existing family
push @{ $HoL{"flintstones"} }, "wilma", "betty";
# one element
$HoL{flintstones}[0] = "Fred";
# another element
$HoL{simpsons}[1] =~ s/(\w)/\u$1/;
# print the whole thing
foreach $family ( keys %HoL ) {
print "$family: @{ $HoL{$family} }\n"
}
# print the whole thing with indices
foreach $family ( keys %HoL ) {
print "family: ";
foreach $i ( 0 .. $#{ $HoL{$family} ) {
print " $i = $HoL{$family}[$i]";
}
print "\n";
}
# print the whole thing sorted by number of members
foreach $family ( sort { @{$HoL{$b}} <=> @{$HoL{$b}} } keys %HoL ) {
print "$family: @{ $HoL{$family} }\n"
}
# print the whole thing sorted by number of members and name
foreach $family ( sort { @{$HoL{$b}} <=> @{$HoL{$a}} } keys %HoL ) {
print "$family: ", join(", ", sort @{ $HoL{$family}), "\n";
}
@LoH = (
{
Lead => "fred",
Friend => "barney",
},
{
Lead => "george",
Wife => "jane",
Son => "elroy",
},
{
Lead => "homer",
Wife => "marge",
Son => "bart",
}
);
# reading from file
# format: LEAD=fred FRIEND=barney
while ( <> ) {
$rec = {};
for $field ( split ) {
($key, $value) = split /=/, $field;
$rec->{$key} = $value;
}
push @LoH, $rec;
}
# reading from file
# format: LEAD=fred FRIEND=barney
# no temp
while ( <> ) {
push @LoH, { split /[\s+=]/ };
}
# calling a function that returns a key,value list, like
# "lead","fred","daughter","pebbles"
while ( %fields = getnextpairset() )
push @LoH, { %fields };
}
# likewise, but using no temp vars
while (<>) {
push @LoH, { parsepairs($_) };
}
# add key/value to an element
$LoH[0]{"pet"} = "dino";
$LoH[2]{"pet"} = "santa's little helper";
# one element
$LoH[0]{"lead"} = "fred";
# another element
$LoH[1]{"lead"} =~ s/(\w)/\u$1/;
# print the whole thing with refs
for $href ( @LoH ) {
print "{ ";
for $role ( keys %$href ) {
print "$role=$href->{$role} ";
}
print "}\n";
}
# print the whole thing with indices
for $i ( 0 .. $#LoH ) {
print "$i is { ";
for $role ( keys %{ $LoH[$i] } ) {
print "$role=$LoH[$i]{$role} ";
}
print "}\n";
}
# print the whole thing one at a time
for $i ( 0 .. $#LoH ) {
for $role ( keys %{ $LoH[$i] } ) {
print "elt $i $role is $LoH[$i]{$role}\n";
}
}
%HoH = (
"flintstones" => {
"lead" => "fred",
"pal" => "barney",
},
"jetsons" => {
"lead" => "george",
"wife" => "jane",
"his boy"=> "elroy",
}
"simpsons" => {
"lead" => "homer",
"wife" => "marge",
"kid" => "bart",
);
# reading from file
# flintstones: lead=fred pal=barney wife=wilma pet=dino
while ( <> ) {
next unless s/^(.*?):\s*//;
$who = $1;
for $field ( split ) {
($key, $value) = split /=/, $field;
$HoH{$who}{$key} = $value;
}
}
# reading from file; more temps
while ( <> ) {
next unless s/^(.*?):\s*//;
$who = $1;
$rec = {};
$HoH{$who} = $rec;
for $field ( split ) {
($key, $value) = split /=/, $field;
$rec->{$key} = $value;
}
}
# calling a function that returns a key,value hash
for $group ( "simpsons", "jetsons", "flintstones" ) {
$HoH{$group} = { get_family($group) };
}
# likewise, but using temps
for $group ( "simpsons", "jetsons", "flintstones" ) {
%members = get_family($group);
$HoH{$group} = { %members };
}
# append new members to an existing family
%new_folks = (
"wife" => "wilma",
"pet" => "dino";
);
for $what (keys %new_folks) {
$HoH{flintstones}{$what} = $new_folks{$what};
}
# one element
$HoH{"flintstones"}{"wife"} = "wilma";
# another element
$HoH{simpsons}{lead} =~ s/(\w)/\u$1/;
# print the whole thing
foreach $family ( keys %HoH ) {
print "$family: ";
for $role ( keys %{ $HoH{$family} } {
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
# print the whole thing somewhat sorted
foreach $family ( sort keys %HoH ) {
print "$family: ";
for $role ( sort keys %{ $HoH{$family} } {
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
# print the whole thing sorted by number of members
foreach $family ( sort { keys %{$HoH{$b}} <=> keys %{$HoH{$b}} } keys %HoH ) {
print "$family: ";
for $role ( sort keys %{ $HoH{$family} } {
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
# establish a sort order (rank) for each role
$i = 0;
for ( qw(lead wife son daughter pal pet) ) { $rank{$_} = ++$i }
# now print the whole thing sorted by number of members
foreach $family ( sort { keys %{$HoH{$b}} <=> keys %{$HoH{$b}} } keys %HoH ) {
print "$family: ";
# and print these according to rank order
for $role ( sort { $rank{$a} <=> $rank{$b} keys %{ $HoH{$family} } {
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
$rec = {
STRING => $string,
LIST => [ @old_values ],
LOOKUP => { %some_table },
FUNC => \&some_function,
FANON => sub { $_[0] ** $_[1] },
FH => \*STDOUT,
};
print $rec->{STRING};
print $rec->{LIST}[0];
$last = pop @ { $rec->{LIST} };
print $rec->{LOOKUP}{"key"};
($first_k, $first_v) = each %{ $rec->{LOOKUP} };
$answer = &{ $rec->{FUNC} }($arg);
$answer = &{ $rec->{FANON} }($arg1, $arg2);
# careful of extra block braces on fh ref
print { $rec->{FH} } "a string\n";
use FileHandle;
$rec->{FH}->autoflush(1);
%TV = (
"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, },
],
},
"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, },
],
},
"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, },
],
},
);
# 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 datastructures if declared as data, so
# sometimes it's easiest to do that
# here's a piece by piece build up
$rec = {};
$rec->{series} = "flintstones";
$rec->{nights} = [ find_days() ];
@members = ();
# assume this file in field=value syntax
while () {
%fields = split /[\s=]+/;
push @members, { %fields };
}
$rec->{members} = [ @members ];
# 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 examples
# if you wanted a {kids} field that was an array reference
# to a list 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;
}
}
# REMEMBER: $rec and $TV{$family} point to same data!!
$rec->{kids} = [ @kids ];
}
# you copied the list, but the list 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
print $TV{simpsons}{members}[2]{age};
# because $TV{simpsons}{kids}[0] and $TV{simpsons}{members}[2]
# both point to the same underlying anonymous hash table
# 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 ";
print scalar ( @{ $TV{$family}{kids} } ), " kids named ";
print join (", ", map { $_->{name} } @{ $TV{$family}{kids} } );
print "\n";
}