Collections of Records

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
--------------
%

Sorting Based on a Key Field

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
%

References to References

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
%

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.80.122