A record is a set of related data fields. For example, the name, address, and phone number are a set of fields that, when taken together, form a payroll record. Keep in mind that Perl does not have any formal treatment of records, and thus the programmer must be creative. Most programmers keep track of records by using anonymous hashes. If you are a C programmer, you can appreciate the similarity between record creation in C and Perl. The C approach might be
struct record { char name[100]; int age; }; typedef struct record RECORD; RECORD create(char *name, int age) { RECORD r; strcpy(r.name, name); r.age = age; return r; } main() { struct record a,b; a = create("Mike",54); printf("%s %d ", a.name, a.age); }
In Perl, the record would be constructed as
sub create { my($name,$age) = @_; my $rec = { name => $name, age => $age }; $rec; } $a = create("Mike",54); print $a -> {name}, " "; print $a -> {age}, " ";
create is a function that inserts its input into a hash keyed by meaningful field names: name and age. The important issue is that the hash is anonymous, and thus the result is stored in a reference. When the subroutine returns this reference, the “record” is created.
Collections of these records can be built and references to them can be stored in lists. Then they can be manipulated using Perl's powerful list manipulation functions, such as push, pop, shift, and unshift.
The next program reads a file of records and builds a list of references. We will assume that the file contains lines of space-separated triplets, such as “name, age, city.” See the folder GetRecs and the input file RECDATA.
% display recdata Mike 57 Columbia Susan 40 Columbia Bob 57 Plantation Erin 28 Annapolis Patti 36 Columbia Tom 36 Columbia Maria 40 Pikesville Dave 36 Odenton % type getrecs.pl # # getrecs.pl # sub create { my($name, $age, $city) = @_; my $emp = { name => $name, age => $age, city => $city }; return $emp; } open(INPUT, "RECDATA") || die "can't find RECDATA "; while(<INPUT>) { @info = split; $record = create(@info); push(@list, $record); } while(@list) { $x = pop(@list); print "RECORD #: ", ++$ct, " "; print "NAME: $x->{name} "; print "AGE: $x->{age} "; print "CITY: $x->{city} "; print "-------------- "; } %
The main part of the program reads lines from the input file and splits them on white space. These values are sent to the create method, where a hash reference is created and filled with this data. Each reference is added to the @list array. The rest of the program is simply a loop, which pops off one reference at a time from the @list array until it is empty. Each record is then printed a field at a time. Later we will see how hashes are used in object-oriented programming. Here is the execution of the program.
% perl getrecs.pl RECORD #: 1 NAME: Dave AGE: 36 CITY: Odenton -------------- RECORD #: 2 NAME: Maria AGE: 40 CITY: Pikesville -------------- RECORD #: 3 NAME: Tom AGE: 36 CITY: Columbia -------------- RECORD #: 4 NAME: Patti AGE: 36 CITY: Columbia -------------- RECORD #: 5 NAME: Erin AGE: 28 CITY: Annapolis -------------- RECORD #: 6 NAME: Bob AGE: 57 CITY: Plantation -------------- RECORD #: 7 NAME: Susan AGE: 40 CITY: Columbia -------------- RECORD #: 8 NAME: Mike AGE: 57 CITY: Columbia -------------- %
Now suppose we want to sort this record based on a particular field. We know from previous experience with sorting that we will have to write some custom subroutines.
@keys = sort custom_sub (@list);
In the above line, the sort routine will repeatedly pass two references to its comparison algorithm. The trick is to have the reference point to the field that we want sorted. Based on how these fields compare, the references are placed in an order that reflects that comparison. Here's the program that sorts on the name field. See the folder NameSort.
% type namesort.pl # # namesort.pl # sub create { my($name, $age, $city) = @_; my $emp = { name => $name, age => $age, city => $city }; return $emp; } open(INPUT, "RECDATA") || die "can't open RECDATA "; while(<INPUT>) { @info = split; $record = create(@info); push(@list, $record); } sub byname { $a->{name} cmp $b->{name}; } @sorted = sort byname(@list); print "SORTED BY NAME "; foreach $item (@sorted) { print "$$item{name} $$item{age} $$item{city} "; } % perl namesort.pl SORTED BY NAME Bob 57 Plantation Dave 36 Odenton Erin 28 Annapolis Maria 40 Pikesville Mike 57 Columbia Patti 36 Columbia Susan 40 Columbia Tom 36 Columbia %
If we want to sort by age, we simply change the special comparison function to
sub byage { $a->{age} <=> $b->{age}; } @sorted = sort byage(@list); % perl agesort.pl SORTED BY AGE Erin 28 Annapolis Tom 36 Columbia Dave 36 Odenton Patti 36 Columbia Susan 40 Columbia Maria 40 Pikesville Mike 57 Columbia Bob 57 Plantation %
Data structures can be arbitrarily complex. In most of the previous examples, the references that we used were pointing to data. However, there is no reason why they cannot point to other references that point to data. The next example involves teams and their players. Each team has a name, a nickname, and a set of players. Each player has a name and a position. A player can be built with a reference to an anonymous hash.
First, we create a few players.
sub makeplayer { my($name, $position) = @_; my $pass = { name => $name, position => $position, }; $pass; } $p1 = makeplayer(Dixon, "Shooting guard"); $p2 = makeplayer(Blake, "Point guard"); $p3 = makeplayer(Baxter, "Center"); $p4 = makeplayer(Mouton, "Small forward"); $p5 = makeplayer(Wilcox, "Power forward");
Each item above is a reference to a player. Now we can build a team that contains these players. Each team will contain a name, a nickname, and a set of players
sub maketeam { my($name, $nickname, @players) = @_; my $team = { name => $name, nickname => $nickname, players => [@players] }; $team; } @players = ($p1, $p2, $p3, $p4, $p5); $team = maketeam("U of Maryland", Terps, @players);
Now we create a function called roster. This function receives a reference to a team and prints information about the players on that team.
sub roster { my ($f) = @_; my ($log) = $f->{players}; print $f->{name}, " ($f->{nickname})", " "; foreach $p (@$log) { print $p->{name}, " "; print $p->{position}, " "; } }
When roster is invoked, it stores the team reference and then obtains a reference to the players:
my($log) = $f->{players};
After the name and nickname are printed, the function loops through the players array.
foreach $p (@$log) { print $p->{name}, " "; print $p->{position}, " "; }
Here is the entire code minus the roster subroutine. See the folder Teams.
# teams.pl # sub makeplayer { my($name, $position) = @_; my $pass = { name => $name, position => $position, }; $pass; } sub maketeam { my ($name, $nickname, @players) = @_; my $team = { name => $name, nickname => $nickname, players => [@players] }; $team; } $p1 = makeplayer(Dixon, "Shooting guard"); $p2 = makeplayer(Blake, "Point guard"); $p3 = makeplayer(Baxter, "Center"); $p4 = makeplayer(Mouton, "Small forward"); $p5 = makeplayer(Wilcox, "Power forward"); @players = ($p1, $p2, $p3, $p4, $p5); $team = maketeam("U of Maryland", Terps, @players); roster($team); % perl teams.pl U of Maryland (Terps) Dixon Shooting guard Blake Point guard Baxter Center Mouton Small forward Wilcox Power forward %
We could create a further level of complexity by creating a few more teams and then creating an array of teams. Finally, we could pass a pattern and the teams array to a function that prints those players matching the pattern. The program below uses this function to print the guards on all the teams. See the folder Players.
% type players.pl # # players.pl # sub printplayers { my ($pat, @teams) = @_; foreach $team (@teams) { my ($log) = $team->{players}; print $team->{name}, "($team->{nickname})", " "; foreach $p (@$log) { if ( $p->{position} =~ /$pat/ ) { print " ", $p->{name}, " "; print $p->{position}, " "; } } } } $p1 = makeplayer(Dixon, "Shooting guard"); $p2 = makeplayer(Blake, "Point guard"); $p3 = makeplayer(Baxter, "Center"); $p4 = makeplayer(Mouton, "Small forward"); $p5 = makeplayer(Wilcox, "Power forward"); @players1 = ($p1, $p2, $p3, $p4, $p5); $team1 = maketeam("U of Maryland", Terps, @players1); $p6 = makeplayer(Williams, "Shooting guard"); $p7 = makeplayer(Duhon, "Point guard"); $p8 = makeplayer(Boozer, "Center"); $p9 = makeplayer(Dunleavy, "Small forward"); $p10 = makeplayer(Jones, "Power forward"); @players2 = ($p6, $p7, $p8, $p9, $p10); $team2 = maketeam("Duke U", "Blue Devils", @players2); @teams = ($team1, $team2); printplayers(guard, @teams); % perl players.pl U of Maryland (Terps) Dixon Shooting guard Blake Point guard Duke U (Blue Devils) Williams Shooting guard Duhon Point guard %
18.222.80.122