Beginning Perl Lesson 7

Table of Contents

Lists and arrays

Lists

A list is a comma-delimited set of values placed in parentheses. In Perl, you create a list by separating the values with commas and surrounding the entire set with parentheses, as in the following examples:

( 1.7, 2.3, 5.5, 4.2, 0.7, 3.3 )
( 'Dopey', 'Grumpy', 'Doc', 'Happy', 'Bashful', 'Sneezy', 'Sleepy' )
( 'yellow', 2.3, undef(), "science\n", sqrt( 2 ), 2237 )

The first list contains only floating point numbers. The second list contains only strings. In general, you will create lists like this that contain data of a single type.

However, you can mix data types and even put undefined values into lists. In the last list above, the first value is the string 'yellow'. The second value is a floating point number, 2.3. The third value is the result of a call to the undef() function, which returns an undefined value. This is how you can insert an undefined value into a list. The fourth value is a string with a newline character at the end, "science\n". The fifth value is the result of a call to the sqrt() function. And the sixth value is an integer, 2237.

Assigning lists to scalar variables

The items in a list can be assigned to scalar variables in a one for one manner, like this:

( $color, $float, $empty, $profession, $squareRoot, $volume ) =
    ( 'yellow', 2.3, undef(), "science\n", sqrt( 2 ), 2237 );

There are two lists here. The first list is a list of scalar variables. The second list is a list of values.

As we remember from an earlier class, a scalar variable can hold exactly one value. So after the assignment, we find that each item from the list has been assigned to each scalar variable. This means $color will contain the value 'yellow', $float the value 2.3, and so on.

Array variables

An array variable is a container that holds a list. Array variables are useful for holding related items. For example, you could put all the items from a row in a spreadsheet into an array variable.

The name of an array variable begins with the @ (at) symbol.

An array variable can be initialized from a list. If we want to initialize an array variable with one of the lists we created above, we do it like this:

@items = ( 'yellow', 2.3, undef(), "science\n", sqrt( 2 ), 2237 );

Here’s an example where we initialize an array with a list of colors:

@colors = ( 'red', 'blue', 'green', 'brown', 'yellow', 'orange' );

Here’s an example with numbers:

@values = ( 1.75, 2.33, 2.75, 1.50, 3.00, 2.50 );

And here’s an example where we initialize an array with values taken from other variables:

$red    = 'red';
$green  = 'green';
$blue   = 'blue';
@colors = ( $red, $green, $blue );

Using scalar() to find the number of elements in an array

The most explicit way to find the number of elements in array variable is to use the scalar() function, like this:

@colors = ( 'red', 'blue', 'green', 'brown', 'yellow', 'orange' );
$numberOfColors = scalar( @colors );

After Perl executes these two lines, $numberOfColors will contain the value 6.

Using individual elements of an array

You can think of the array variable as a box with numbered compartments. When you put items into an array, each item goes into its own numbered compartment. A numbered compartment can hold exactly one item. Each item in an array can be called an array element. The number of the compartment is the index corresponding to the position of the compartment in the box. You can get one item out of an array by referring to the name of the box (using the name of the array variable) and the number of the compartment (the element index).

There are three things to remember about accessing individual elements of an array:

Here’s an example of getting individual elements from an array:

@colors      = ( 'red', 'blue', 'green', 'brown', 'yellow', 'orange' );
$firstColor  = $colors[ 0 ];
$secondColor = $colors[ 1 ];

You have to learn from hard experience that you need to put a $ symbol in front of the variable name and that the indices start with 0, not 1.

Using more than one element from an array

It is simple to copy the values of one array variable to another array variable:

@colors = ( 'red', 'blue', 'green', 'brown', 'yellow', 'orange' );
@colorNames = @colors;

It is possible to pull more than one item out of an array at a time. One way is to use the range operator, ... Here’s an example where we get the first two elements from the array by using the range 0 .. 1, which gets all elements beginning at element 0 and ending at element 1:

@colors = ( 'red', 'yellow', 'green', 'blue' );
@warmColors = @colors[ 0 .. 1 ];

@warmColors will contain the values ( 'red', 'yellow' ). Note that since we’re getting more than one element from @colors, we have to place them into an array variable, here @warmColors.

If you want more than one element from the array, but the elements you want are not contiguous, then you can designate the elements you want in a comma-separated list, like this:

@colors = ( 'red, 'green', 'blue', 'yellow' );
@warmColors = @colors[ 0, 3 ];

@warmColors will contain the values ( 'red', 'yellow' ).

Finally, it’s possible to get elements of an array and put them into scalar variables. You have to make sure you have a scalar variable for each element. Here’s how you do it:

@colors = ( 'red', 'yellow', 'green', 'blue' );
( $red, $yellow, $green, $blue ) = @colors;

Note that the comma-separated list of scalar variables has to be placed in parentheses for this to work. The parentheses tell Perl that it’s working with arrays.

Pulling some of the items out of an array is called slicing the array.

Initializing individual elements of an array

We have already seen how to initialize all of the elements of an array. But what if we want initialize individual elements? This is done simply by assigning a value to an array element using the index notation:

my( @colors );

$colors[ 0 ]    = 'red';
$colors[ 1 ]    = 'yellow';
$colors[ 3 ]    = 'blue';
@colors[ 4, 5 ] = ( 'pink', 'orange' );

Note that we did not initialize element 2 of the array. This means that this element contains an undefined value.

Adding elements to or removing elements from the ends of an array

Perl provides four functions that make it simple for us to add or remove elements from either end of the array.

Function Name Behavior
push() adds an item onto the end of the array
pop() removes an item from the end of the array
unshift() adds an item to the start of the array
shift() removes an item from the start of the array

Here’s a script that shows how to use shift(), unshift(), pop(), and push().

#!/usr/bin/perl
#
#   arrays.pl
#   13-Jun-2004
#
#   Conrad Halling
#   conrad.halling@sphaerula.com
#
#   This script demonstrates some simple things you can do with arrays.

use warnings;

    my( $color );
    my( @colors );

    #   Initialize an array and show its contents with a print() statement.

    @colors = ( 'red', 'yellow', 'blue' );
    print( "\@colors = '@colors'\n\n" );

    #   Now use push(), pop(), shift(), and unshift() to show what happens
    #   to the array.

    #   shift() removes the value from the start of the array and places it in
    #   the scalar variable.

    print( "shift\n" );
    $color = shift( @colors );
    print( "\$color = $color; \@colors = '@colors'\n\n" );

    #   unshift() adds a value to the start of the array.

    $color = 'green';
    print( "unshift $color\n" );
    unshift( @colors, $color );
    print( "\@colors = '@colors'\n\n" );

    #   pop() removes the value from the end of the array and places it in the
    #   scalar variable. This is like shift(), except it works at the opposite
    #   end.

    print( "pop\n" );
    $color = pop( @colors );
    print( "\$color = $color; \@colors = '@colors'\n\n" );

    #   Push adds the value to the end of the array. This is like
    #   unshift(), except it works at the opposite end.

    $color = 'brown';
    print( "push $color\n" );
    push( @colors, $color );
    print( "\@colors = '@colors'\n\n" );

    #   Print the number of items in the array.

    print( "The array \@colors contains ", scalar( @colors ), " items.\n" );

    #   Print only item 0 of the array.

    print( "Item 0 of \@colors contains '$colors[ 0 ]'.\n" );

The output from this script is:

> perl arrays.pl
@colors = 'red yellow blue'

shift
$color = red; @colors = 'yellow blue'

unshift green
@colors = 'green yellow blue'

pop
$color = blue; @colors = 'green yellow'

push brown
@colors = 'green yellow brown'

The array @colors contains 3 items.
Item 0 of @colors contains 'green'.

Note that this example script shows how to print out the values of variables. This is useful for debugging.

Sorting lists and arrays using sort()

You will create many lists that you’ll want to sort. For example, you might read a list of names from a text file, but before you use the names, you might want to sort them first. We’ll talk about sorting lists alphabetically and numerically.

Sorting an array alphabetically

Suppose we have an array that contains some names. We can sort the names in alphabetical order using Perl’s sort() function, like this:

@sorted = sort( @names );

This put the sorted names into a new array variable, @sorted. If we want to sort the names in place, we do this:

    @names = sort( @names );

Here, sort gets the names from @names, sorts them, then reassigns the sorted names to the original variable, @names.

Here’s an example of a script that sorts alphabetically:

#!/usr/bin/perl
#
#   alphaSort.pl
#   13-Jun-2004
#
#   Conrad Halling
#   conrad.halling@sphaerula.com
#
#   This script initializes an array, prints the contents of the array,
#   sorts the array alphabetically, then prints the sorted contents.

use warnings;

    my( @names );

    @names = ( 'Dopey', 'Grumpy', 'Doc', 'Happy', 'Bashful', 'Sneezy', 'Sleepy' );
    print( "Before sorting, the array is:\n  '@names'.\n" );
    @names = sort( @names );
    print( "After sorting, the array is:\n  '@names'.\n" );

The output from this script is:

> perl alphaSort.pl
Before sorting, the array is:
  'Dopey Grumpy Doc Happy Bashful Sneezy Sleepy'.
After sorting, the array is:
  'Bashful Doc Dopey Grumpy Happy Sleepy Sneezy'.

Sorting an array numerically

Sorting an array numerically is a little trickier. I’m going to give you the formula without explaining it for now. If you want to know more about sorting, read pp. 115-120 in Perl Cookbook or pp. 789-793 in Programming Perl, 3rd edition.

@numbers = ( 1.5, 2.3, 5.4, 4.3, 1.7 );
@numbers = sort { $a <=> $b } @numbers;

And here’s a working script:

#!/usr/bin/perl
#
#   numericalSort.pl
#   13-Jun-2004
#
#   Conrad Halling
#   conrad.halling@sphaerula.com
#
#   This script initializes an array variable with some numbers, prints the
#   array, sorts the array numerically, then prints the array again.

use warnings;

    my( @numbers );

    @numbers = ( 1.5, 2.3, 5.4, 4.3, 1.7 );
    print( "Before sorting, the array is '@numbers'.\n" );
    @numbers = sort { $a <=> $b } @numbers;
    print( "After sorting, the array is '@numbers'.\n" );

The output of this script is:

> perl numericalSort.pl
Before sorting, the array is '1.5 2.3 5.4 4.3 1.7'.
After sorting, the array is '1.5 1.7 2.3 4.3 5.4'.

Looping using foreach

We have already learned how to loop in a program using while. Now we’re going to learn how to loop through a list or array, using each item.

It’s common to want to use each item in an array in order. Perl lets us do that with the foreach command, which looks like this:

@colors = ( 'red', 'green', 'blue', 'orange' );
foreach $color ( @colors )
{
    print( "$color\n" );
}

foreach steps through the array, one item at a time, and places the item into the indicated scalar variable. Then Perl executes the code in the braces that follow the foreach statement. When Perl has exhausted the array, it stops looping.

foreach also steps through lists, like this:

foreach $color ( 'red', 'green', 'blue', 'orange' )
{
    print( "$color\n" );
}

The pragma use warnings; revisited

In an earlier class, we learned about the use warnings pragma. A pragma changes the behavior of Perl.

Now we’re ready to see how using this pragma can help us find bugs caused by typographical errors in our scripts.

Here’s a script in which we don’t use the warnings pragma. This script is perfectly good Perl, except that it contains a single typographical error.

This example shows how the pragma use warnings; can save us time when we make typographical errors. If you see a warning about a variable being used only once, look at it closely to see if you simply spelled the variable name incorrectly.

#!/usr/bin/perl
#
#   typo1.pl
#   13-Jun-2004
#
#   Conrad Halling
#   conrad.halling@sphaerula.com
#
#   This script demonstrates how omitting the use warnings pragma can
#   prevent us from finding problems with our scripts. Here, we use
#   the variable @Colors when we mean to use the variable @colors.

    my( $color );
    my( @colors );

    @colors = ( 'red', 'blue', 'green' );
    foreach $color ( @Colors )
    {
        print( "$color\n" );
    }

If we run this script, we see the following output:

> perl typo1.pl
>

There is no output! What happened to our colors?

Let’s modify the script by adding use warnings;.

#!/usr/bin/perl
#
#   typos2.pl
#   13-Jun-2001
#
#   Conrad Halling
#   conrad.halling@sphaerula.com
#
#   This script demonstrates how adding  the use warnings pragma can
#   help us find problems with our scripts. Here, we use
#   the variable @Colors when we mean to use the variable @colors.

use warnings;

    my( $color );
    my( @colors );

    @colors = ( 'red', 'blue', 'green' );
    foreach $color ( @Colors )
    {
        print( "$color\n" );
    }

This time, the output is:

> perl typo2.pl
Name "main::Colors" used only once: possible typo at typo2.pl line 20.
>

The warning tells us that the variable @Colors was used only once, on line 17. Sure enough, what we meant to use here was @colors, with a lower-case c. After we correct this error, the script runs correctly.

A summary example script

The numberSort.pl script below puts together everything we covered in the last few classes. This script asks the user for the name of an input file containing a list of numbers. Then the script asks the user for the name of an output file where the results will be written. The script opens and reads the input file of numbers, one number on each line, and places each number into an array variable. The script then sorts the array numerically and writes the numbers into the output file, one number on each line.

Here’s a computer-generated list of numbers for sorting. Copy this list from the web page and save it in a text file on your computer.

2.03
8.45
4.99
4.27
9.00
0.83
0.68
9.87
8.49
9.16
2.85
5.84
7.76
7.58
1.90
8.20
4.87
3.73
1.90
5.03

And here’s our script:

#!/usr/bin/perl

#   numberSort.pl
#   13-Jun-2004
#
#   Conrad Halling
#   conrad.halling@sphaerula.com
#
#   This script reads a file of numerical values, where each numerical
#   value appears on its own line. The script sorts the values and writes
#   them to an output file.

use warnings;

    my( $dataLine );
    my( $inFileName );
    my( $number );
    my( @numbers );
    my( $outFileName );
    my( $success );

    #   Get the name of the input file.
    #   Try to open the file.

    print( "\n" );
    print( "Enter the name of the input file.\n" );
    $inFileName = <>;
    chomp( $inFileName );
    $success = open( IN, "<$inFileName" );
    if ( ! $success )
    {
        die( "Can't open file $inFileName for reading: $!.\n\n" );
    }

    #   Get the name of the output file.
    #   Try to open the file.

    print( "\n" );
    print( "Enter the name of the output file.\n" );
    $outFileName = <>;
    chomp( $outFileName );
    $success = open( OUT, ">$outFileName" );
    if ( ! $success )
    {
        die( "Can't open file $outFileName for writing: $!.\n\n" );
    }

    #   Read each number from the input file and put it into the array.

    while ( defined( $dataLine = <IN> ) )
    {
        chomp( $dataLine );
        push( @numbers, $dataLine );
    }
    close( IN );

    #   Sort @numbers numerically.

    @numbers = sort { $a <=> $b } @numbers;

    #   Write each number to the output file on its own line.

    foreach $number ( @numbers )
    {
        print( OUT "$number\n" );
    }
    close( OUT );

Here’s what the output looks like:

> perl numberSort.pl

Enter the name of the input file.
unsorted.txt

Enter the name of the output file.
sorted.txt
> cat sorted.txt
0.68
0.83
1.90
1.90
2.03
2.85
3.73
4.27
4.87
4.99
5.03
5.84
7.58
7.76
8.20
8.45
8.49
9.00
9.16
9.87
>

Homework assignment

I have provided below a list of ten words, one on each line. Copy this list from the web page and save it to a text file. Write a script that reads this file and puts the words into an array variable. Sort the array. Write the words in the sorted array to an output file, one word on each line. Use the summary script above as a guide to writing this script.

nullify
deliquescent
aqueous
glabrous
mathematics
disambiguate
ambidextrous
parsimony
reputation
signify