Wednesday, August 8, 2007

Making Perl Reusable with Modules


Perl software development can occur at several levels. When first developing the idea for an application, a Perl developer may start with a short program to flesh out the necessary algorithms. After that, the next step might be to create a package to support object-oriented development. The final work is often to create a Perl module for the package to make the logic available to all parts of the application. Andy Sylvester explores this topic with a simple mathematical function.

Creating a Perl Subroutine

I am working on ideas for implementing some mathematical concepts for a method of composing music. The ideas come from the work of Joseph Schillinger. At the heart of the method is being able to generate patterns using mathematical operations and using those patterns in music composition. One of the basic operations described by Schillinger is creating a "resultant," or series of numbers, based on two integers (or "generators"). Figure 1 shows a diagram of how to create the resultant of the integers 5 and 3.

creating the resultant of 5 and 3
Figure 1. Creating the resultant of 5 and 3

Figure 1 shows two line patterns with units of 5 and units of 3. The lines continue until both lines come down (or "close") at the same time. The length of each line corresponds to the product of the two generators (5 x 3 = 15). If you draw dotted lines down from where each of the two generator lines change state, you can create a third line that changes state at each of the dotted line points. The lengths of the segments of the third line make up the resultant of the integers 5 and 3 (3, 2, 1, 3, 1, 2, 3).

Schillinger used graph paper to create resultants in his System of Musical Composition. However, another convenient way of creating a resultant is to calculate the modulus of a counter and then calculate a term in the resultant series based on the state of the counter. An algorithm to create the terms in a resultant might resemble:

Read generators from command line
Determine total number of counts for resultant
(major_generator * minor_generator)
Initialize resultant counter = 0
For MyCounts from 1 to the total number of counts
Get the modulus of MyCounts to the major and minor generators
Increment the resultant counter
If either modulus = 0
Save the resultant counter to the resultant array
Re-initialize resultant counter = 0
End if
End for

From this design, I wrote a short program using the Perl modulus operator (%):

#!/usr/bin/perl
#*******************************************************
#
# FILENAME: result01.pl
#
# USAGE: perl result01.pl major_generator minor_generator
#
# DESCRIPTION:
# This Perl script will generate a Schillinger resultant
# based on two integers for the major generator and minor
# generator.
#
# In normal usage, the user will input the two integers
# via the command line. The sequence of numbers representing
# the resultant will be sent to standard output (the console
# window).
#
# INPUTS:
# major_generator - First generator for the resultant, input
# as the first calling argument on the
# command line.
#
# minor_generator - Second generator for the resultant, input
# as the second calling argument on the
# command line.
#
# OUTPUTS:
# resultant - Sequence of numbers written to the console window
#
#**************************************************************

use strict;
use warnings;

my $major_generator = $ARGV[0];
my $minor_generator = $ARGV[1];

my $total_counts = $major_generator * $minor_generator;
my $result_counter = 0;
my $major_mod = 0;
my $minor_mod = 0;
my $i = 0;
my $j = 0;
my @resultant;

print "Generator Total = $total_counts\n";

while ($i < $total_counts) { $i++; $result_counter++; $major_mod = $i % $major_generator; $minor_mod = $i % $minor_generator; if (($major_mod == 0) || ($minor_mod == 0)) { push(@resultant, $result_counter); $result_counter = 0; } print "$i \n"; print "Modulus of $major_generator is $major_mod \n"; print "Modulus of $minor_generator is $minor_mod \n"; } print "\n"; print "The resultant is @resultant \n";

Run the program with 5 and 3 as the inputs (perl result01.pl 5 3):

Generator Total = 15
1
Modulus of 5 is 1
Modulus of 3 is 1
2
Modulus of 5 is 2
Modulus of 3 is 2
3
Modulus of 5 is 3
Modulus of 3 is 0
4
Modulus of 5 is 4
Modulus of 3 is 1
5
Modulus of 5 is 0
Modulus of 3 is 2
6
Modulus of 5 is 1
Modulus of 3 is 0
7
Modulus of 5 is 2
Modulus of 3 is 1
8
Modulus of 5 is 3
Modulus of 3 is 2
9
Modulus of 5 is 4
Modulus of 3 is 0
10
Modulus of 5 is 0
Modulus of 3 is 1
11
Modulus of 5 is 1
Modulus of 3 is 2
12
Modulus of 5 is 2
Modulus of 3 is 0
13
Modulus of 5 is 3
Modulus of 3 is 1
14
Modulus of 5 is 4
Modulus of 3 is 2
15
Modulus of 5 is 0
Modulus of 3 is 0

The resultant is 3 2 1 3 1 2 3

This result matches the resultant terms as shown in the graph in Figure 1, so it looks like the program generates the correct output.

No comments: