Developer

Generate multilevel reports and simulate static variables in Perl

Here's a pair of Perl tips to make your life easier. Setting up multilevel control breaks may not be on your fun list, but we'll show you an easy way to do it. Our second tip explains how to create the effect of a static variable.


By Charles Galpin

When you use Perl to generate reports that break at multiple levels, you can use hashes (even hashes of hashes) to sort your data into the order you need. To illustrate this approach, let's consider some code that reads a source file (named Infile) containing pipe-delimited product sales records. The program sorts sales first by store and then by product, totals sales for each store and totals all sales, and prints a report.

The file contents, from left to right, are store number, product, sale quantity, and total sale price. The same product can be listed several times for each store. A record might look like this:
    123|Deluxe Widget|3|12.99

The following code will place each record into a hash in which the first key is the store number, the second key is the product, and the final key is either qty (sum of quantities sold) or price (selling price) for each product.
    # open data file reading each line into hash %h
    open IN, 'infile';
    while(<IN>) {
        @s = split /\|/, $_;
        $h{$s[0]}{$s[1]}{qty} += $s[2];
        $h{$s[0]}{$s[1]}{price} += $s[3];
    }
    close IN;


The rest of the program prints the report with data sorted first by store and then by product. To accomplish this, it uses two nested loops, traversing the first two hash keys. The outer loop prints the headers and subtotals, and the inner loop prints the details. Listing A shows this code.

A more formal reporting system would include additional data, add page-breaking logic, and probably include more levels of data control breaks, but this example demonstrates the basic technique.

Implementing "static" variables
If you're looking for a way to provide private, permanent storage to functions in Perl, you won't find the static declaration that other languages have. However, because of the way Perl handles garbage collection, you can achieve the same effect. Lexical variables (declared with my) are not automatically recycled when their scope ends but rather when they are no longer used. Therefore, by placing a block around both the my declaration and the subroutine, the variable can be seen only within that block, but its value persists between subsequent calls to the subroutine.

The following code sample illustrates how this approach works. The BEGIN construct is used on the block to execute the code before the rest of the program, thus making sure the "static" is initialized before the subroutine is used.
   
    my $answer = 42;
    print "$answer\n";
    for(0..4) { print answer(), "\n" }
    print "$answer\n";
   print answer(), "\n";

    BEGIN {
        my $answer = 1;  # "static" variable
        sub answer {
            return $answer++;
        }
    }


Running the code generates the following output:
    42
    1
    2
    3
    4
    5
    42
    6


The sample output illustrates how the scope and persistence of the variable enable you to imitate a static type variable.

Editor's Picks

Free Newsletters, In your Inbox