Monday, March 24, 2003

Lists and Arrays (S&P -- Chapter 3)

Like C's arrays, perl supports the concept of an array, which can be though of as a list of elements. Like C++'s vector class, perl's arrays grow as required, so there is no need to worry about memory allocation issues when store elements in a perl array. To declare an array variable, we use the special prefix character @. We can iterate over the array by using a foreach loop:


#!/usr/bin/perl -w

use strict;

my @a;

$a[0] = 'a';
$a[3] = 'b';
$a[7] = 'c';

print "Iteration #1\n";
foreach my $v (@a) {
	print defined $v ? "$v " : "undef ";
}
print "\n";

print "\nIteration #2\n";
foreach my $i (0..$#a) {
	print "\$a[$i] = ", defined $a[$i] ? "$a[$i]" : "undef", "\n";
}
print "\n";

# simpler:

print "Iteration #3 (using \$_ default)\n";
for (@a) {
	print defined $_ ? "$_ " : "undef ";
}
print "\n";

print "\nIteration #4 (using \$_ default)\n";
for (0..$#a) {
	print "\$a[$_] = ", defined $a[$_] ? $a[$_] : "undef", "\n";
}


There are several things to note about the above program:

List literals, push/pop and shift/unshift

It is common in perl to initialize an entire array with a list literal rather than initializing each element individually. To do this, we can use a list literal, which is basically a list of scalar values separated by commas and enclosed in parenthesis. For example:


#!/usr/bin/perl -w

use strict;

my @a = ('def', 123);

push @a, 456;
unshift @a, 'xyz';

print "@a\n";
print @a, "\n";

print "Popped ", pop @a, " from \@a \n";
print "Shifted ", shift @a, " from \@a\n";

print "@a\n";


Given the initial definition of @a, we have $a[0] == 'def' and $a[1] == 123. This is more efficient than initializing each array element separately.

We can add elements to the back or front of an array by using the push and unshift functions, respectively. note that like push_back() for C++ vectors, we don't have to worry about ensuring there is enough room in the perl array when doing these operations -- perl takes care of it for us. The second argument to push and unshift is actually a list. Therefore we can use push and shift to add a list of elements to an array. In the above example, the second (scalar) arguments are converted to lists of one element which is then added to the @a array.

The next two print lines show that we can actually display the values in an array. While the two lines look similar, their output is different:

print "@a\n";	# displays: abc def 123 456
print @a, "\n"; # displays: abcdef123456

When an array is displayed inside double quotes each array element is displayed with a space separating each element. When an array is printed outside of the quotes, the array elements are displayed without an interleaving space character.

pop and unshift basically do the opposite of push and shift, respectively. Each function will remove one element from either the back or front of the array and return that value as a result.

The qw short cut, list assignment and list/scalar context

Using the ('...', '....', ...) notation described above to create a list literal of string scalars can be quite cumbersome. Therefore perl allows you to create a list literal of strings using the qw construct as demonstrated by the following code:


#!/usr/bin/perl -w

use strict;

my @a = qw/this is a list/;	# equivalent to ('this', 'is', 'a', 'list')

print join(",", @a), "\n";

my ($one, $two) = @a;
print "$one/$two\n";

my ($a, $b, $c, $d, $e) = @a;
print "$a,$b,$c,$d,$e\n";

my ($val1) = @a;

my $val2 = @a;

print "$val1 $val2\n";

@a = sort @a;
print "@a\n";


All the elements of the list are specified between the /.../ delimiters (you can actually use any pair of corresponding delimiters for the qw construct). This notation makes the creation of lists of strings much easier.

The above code also demonstrates the use of the join function which takes a delimiter string and an array. It will create a string which consists of all the elements of the array separated by the specified delimiter string.

The code my ($one, $two) = @a; shows how we can extract elements from a list and assignment them to scalars. In this code fragment, the variable $one will get the first element of the list and $two will get the second element. This is equivalent to saying:

my $one = $a[0];
my $two = $a[1];

If we try to assign more variables than there are elements in the list, then the remaining variables will be left as undef. For example, in the statement my ($a, $b, $c, $d, $e) = @a;, the variable $e will not be defined, since the array has only four elements. Note that when declaring more than one variable with my, you must include the variables inside parenthesis.

The two statements:

my ($val1) = @a;
my $val2 = @a;

demonstrate the importance of list context versus array context. In the first case, $val1, because it is being declared inside parenthesis, is in list context. This means that @a will also be treated as a list. When the assignment happens, $val1 will get the value of the first element of the array @a. In the second assignment, however, $val2 is in scalar context, therefore @a will be evaluated as a scalar. When an array is evaluated as a scalar, it returns the number of elements in the array. Therefore, the statement print "$val1 $val2\n"; will display this 4.

There are other important situations where between list and scalar context arise. See page 51 of S&P for details.

The final couple of statements in the above code demonstrate the use of the sort function which lexicographically sorts an array. Note that there is no harm in using the same variable on the left and right hand side of the assignment when doing the sorting. Note that arrays are lexicographically sorted. Therefore, the statement

print join(",",sort (123, 9, 50)), "\n";

will display 123,50,9 because the list is being sorted alphabetically, and not numerically. Another function called reverse can be used to reverse arrays.

Another use of list assignments is to swap to scalars. For example, the code fragment ($v1, $v2) = ($v2, v1) will swap the values stored in the scalar variables $v1 and $v2.

Last modified: Mon Mar 24 17:17:03 2003