A.10 Answer for Chapter 11

A.10.1 Exercise (Section 11.8.1)

use IO::File;
my %output_handles;
while (<>) {
  unless (/^(\S+):/) {
    warn "ignoring the line with missing name: $_";
  my $name = lc $1;
  my $handle = $output_handles{$name} ||=
    IO::File->open(">$name.info") || die "Cannot create $name.info: $!";
  print $handle $_;

At the beginning of the while loop, use a pattern to extract the person's name from the data line, issuing a warning if that's not found.

Once you have the name, force it to lowercase so that an entry for "Maryann" will get filed in the same place as one for "MaryAnn." This is also handy for naming the files, as the next statement shows.

The first time through the loop, the filehandle must be created. Let's see how to do that. The || operator has a higher precedence than the assignment, so it is evaluated first; the program will die if the file can't be created. The ||= operator assigns the filehandle to the hash, and the = operator passes it to $handle as well.

The next time you have the same name in $name, the ||= operator kicks in. Remember that $gilligan ||= $anything is effectively like $gilligan = $gilligan || $anything. If the variable on the left is a false value (such as undef), it's replaced by the value on the right, but if it's true (such as a filehandle), the value on the right won't even be evaluated. Thus, since the hash already has a value for that person's name, the hash's value is used and assigned directly to $handle without having to (re-)create the file.

It wasn't necessary to code the castaways' names into this program, because they will be read in as data. This is good because any additional castaway won't require having to rewrite the program. If someone's name is accidentally misspelled, however, it puts some of their data into a new file under the wrong name.