EIW Fall 2004 Lecture Notes

Perl Arrays and Associative Arrays


Arrays

Perl supports arrays, which are ordered lists of scalar values. In perl arrays can have any number of elements, and grow automatically as needed! Array names all start with the character @. Here are some possible array names: @names, @ages, @html_tags.

Perl arrays are often called lists.

Array Literals (Constants)

You can create an array constant by enclosing a list of scalar values inside parenthesis, with a comma between each adjacent scalars. For example, the following are all array constants:

(1,2,3)
("Dave Hollinger", "Joe Student", "Bill Clinton", "Homer Simpson")
("Amos Eaton", 200, "Lally", 130, "CII", 500)

An array literal can also include scalar variables as elements:

($name, $age, $weight)

This array constant depends on the values of the three scalar variables referenced. You can use any perl expression that is a scalar as an array element:

($a+$b, $c*$d, $f*100)

The above array has three elements. The empty array is simply () and has zero elements.

List Constructor

Perl includes a notation called a list constructor that provides a quick way to create an array (list) that holds a sequence of integers. Here are a few examples:

(1..5)           this is the same as (1, 2, 3, 4, 5)
(1.5..5.5)       this is the same as (1.5, 2.5, 3.5, 4.5, 5.5)
(10..13,18,21)   this is the same as (10, 11, 12, 13, 18, 21)

print and Array Literals

Many perl operators can take an array as an argument, for example the print operator can handle an array constant like this:

print ("Name is ", $name, " age next year is ", $age+1, "\n" );

The argument given to print is an array literal that has 5 elements, print sends each to stdout in order (with no spaces in between elements). Although this looks like subroutine or function call syntax (if you are used to 'C' or 'C++'), this is just giving the print operator an array instead of a scalar!

Array Variables

Array variables can use the assignment operator just like scalars, so you can do this:

@names = ("Bill", "William", "Billy", "Willy", "Billiam", "Billionaire" );
@jobs = ("CEO", "CFO", "UFO", "CIO", "CIA", "FBI");

@products = ("Apples", "Oranges", "Bananas", "Wireless Internet PDAs");
@prices =   (     .25,       .30,       .40,                 $529.99);

@newprices = @prices;
print @names;

Fancy Stuff

You can do all kinds of fancy stuff with perl arrays, including the following:

@a = (1,2,3);		
@b = ("hello", @a, "bye");    # @b is ("hello",1,2,3,"bye")
@a = (@a,4);                  # @a is now (1, 2, 3, 4)

($x,$y,$z) = (1,2,3);         # same as $x=1; $y=2; $z=3;

($f1, $f2) = @a;	      # now $f1 is 1 and $f2 is 2

Array Element Access

You can access individual elements of an array using the subscript operator []. Since the result of accessing a single element of an array is a scalar, when you use the subscript operator you also use a $ in front of the array name instead of a @. This seems confusing at first, but keep in mind that the result of accessing an array element is a scalar. Just like in C/C++, perl arrays start with a subscript of 0 - so the first element in the array @names is $names[0] and $names[1] is the second element in the array.

Here are some examples of array element access:

@vals = (10, 25, 50, 100, 200);
print ("The first value is ", $vals[0],"\n");

@class_names = ("Exploiting the Information World",
		"Data Structures",
		"Operating Systems",
		"Artificial Intelligence",
		"Programming Languages");

@class_sizes = (33,40,100,33,50);

print("The course ",$class_names[2]," has ",$class_sizes[2]," students\n");

$class_sizes[2] = $class_sizes[2] + 10;

print("The course ",$class_names[2]," has ",$class_sizes[2]," students\n");

Array Slices

You can operate on a subset of an array by using the slice operator. Here are a few examples:


#define a student record for dave (the last three elements are grades)

@studentrec = ("Dave Hollinger", "hollingd@cs.rpi.edu", 123-45-6789,
		34,88,22);


# now just get the grades:

@grades = @studentrec[3,4,5];

# This could also be done the hard way:

@grades = ($students[3], $students[4], $students[5]);

# You can make a slice that contains any of the array elements
# (and each any number of times).

@stuff = @grades[1,1,1,1,1,3..4];
print @stuff;

Notice that when using the slice operator, the result is always an array - so you keep the leading "@". Here are some more examples:

@numbers = (1..10);
@evennumbers = (1..10)[1,3,5,7,9];
@oddnumbers = @numbers[0,2,4,6,8,10];

Array Size

Perl arrays grow automatically as you add elements to the end. If you add an element past the end of a perl array - all the elements that have not been defined get the special perl value undef.

You can get the index value of the last element in an array with the special syntax $#arrayname (note it starts with $ since it is a scalar value). You can get the size of an array (the number of elements in the array) by evaluating @arrayname in a scalar context, that is, in an expression where perl is expecting a scalar value. Some examples:

# define an array
@nums = (1,2,3,5,8,13,21);

# find out the index of the last array element
$lastindex = $#nums;

# now find out how many array elements there are
$arraysize = @nums;

print("The array has $arraysize elements and the last one has index $lastindex\n");

Some Array Operators

Perl includes some array operators:

push and pop can be used to implement a stack (Last-in-First-out) in which the top of the stack corresponds to the end of the array. shift and unshift also implement a stack, but now the front of the array is the top of the stack. You can implement a queue (First-in-First-Out) using push and shift

:

#define an array
@letters = ("c", "l", "q", "t");

# this next line does the same thing as @letters = (@letters,"z");
push(@letters,"z");

# remove the last element
$lastelem = pop(@letters);    #$lastelem is now "z"
                              # and @letters is ("c", "l", "q", "t")
# do it again
$lastelem = pop(@letters);    # now $lastelem is "t"
			      # now @letters is ("c", "l", "q")

# add a new letter to the beginning of an array
unshift(@letters,"a");	      # now @letters is ("a", "c", "l", "q")

# now take the first element off the array
$firstelem = shift(@letters); # $firstelem is "a"
                              # and @letters is now ("c", "l", "q")

# do it again

$firstelem = shift(@letters); # $firstelem is now "c"
                              # and @letters is now ("l", "q")

If you give pop or shift an empty array they return the special perl value undef.

reverse does what you expect - it reverses the order of elements in a perl array. However, reverse doesn't change an array in place - it create a new array! To reverse an array @a you have to do this: @a = reverse(@a);.

sort orders the elements in an array in string comparison order, that is - it uses the string comparison operators to order the elements in an array. Just like reverse, sort doesn't modify a named array - it create a new array that contains the sorted elements. An example:

@animals = ("zebra","lion","dog","grad student","albino squirrel","dolphin");

@ordered = sort( @animals );

# @ordered is ("albino squirrel","dog","dolphin","grad student","lion","zebra");

There are ways to change how perl sorts things (for example to sort numerically instead of as strings), but we need to cover subroutines first.

<STDIN> as an Array

You can assign <STDIN> to an array! This tells perl to read all remaining lines from STDIN and put them in to an array. all remaining lines means until END-OF-FILE is found. An example:

# prompt for use input
print "Enter your name, age and favorite cookie on seperate lines\n";

# read everything from stdin
@lines = <STDIN>;

# chop off newlines using the array chop operator (chops each line)
chop(@lines);

print "Your name is $lines[0]\n";
print "Your age is $lines[1]\n";
print "Your cookie is $lines[2]\n";

another way to do this (perhaps better?)


# read in 3 lines an put in to variables, also chops each one
chop(($name,$age,$cookie) = <STDIN>);

# There might be more in STDIN - if so, we ignore it!

print "\n\n"; # force newline at end of input

print "Your name is $name\n";
print "Your age is $age\n";
print "Your cookie is $cookie\n";

Variable Interpolation of Arrays

Array variables can be interpolated inside double quoted strings. The array name is replaced by a list of the elements with blanks between each. An example:

@problem = ("the",  "lack", "of", "parking");

print "The problem with RPI is @problem\n";

# Will output "The problem with RPI is the lack of parking" and newline.

Array elements can also be interpolated (we've already seen a few examples). The index value can be an expression which is evaluated! Some examples:

# create an array with numbers from 10 to 20
@numbers = (10..20);

# set up a scalar variable
$i = 3;

# print out some stuff showing array element variable interpolation
# will print "$numbers[3] is 13"
print "\$numbers[3] is $numbers[$i]\n";

#will print "$numbers[3+$i] is 16
print "\$numbers[3+\$i] is $numbers[$i+3]\n";

Perl Associative Arrays

Perl also supports associative arrays (sometimes called hashes). An associative array is like an array in that it contains a collection of scalar values that can be accessed by an index. In perl arrays, the index is restricted to integer values starting at 0 (and going up). In perl associative arrays the index can be any scalar value - these indicies are often called keys.

The elements of an associative array have no order, they are just a collection of (key,value) pairs that can be treated as a group. (An associative array is like a set - no order is implied).

Associative Array Variables

Associative array variable names all start with the % character. Although there are associative array operators in perl, typically you deal with individual elements and not an entire assoc. array. To access an individual element you use the {} index operator (instead of the array [] operator), and when accessing the scalar value of an assoc. array element you use a leading $ (just like with array elements). Sounds confusing, so perhaps some examples will help:

# create an element in the associative array %age that has the key
# "John Smith" and the value 45.

$age{"John Smith"} = 45;

# create another one

$age{"Mary Jones"} = 22;

print $age{"John Smith"};	# will print 45

As the above code shows, you can create an element of an associative array simply by assigning a value to it. These associative arrays can be as large as you want (as large as the memory in your computer). Here is another example:

#define an assoc array that can lookup text name of each digit.
$text{0} = "zero";
$text{1} = "one";
$text{2} = "two";
$text{3} = "three";
$text{4} = "four";
$text{5} = "five";
$text{6} = "six";
$text{7} = "seven";
$text{8} = "eight";
$text{9} = "nine";

#define an assoc array that can lookup integer value digit name.
$val{"zero"} = 0;
$val{"one"} = 1;
$val{"two"} = 2;
$val{"three"} = 3;
$val{"four"} = 4;
$val{"five"} = 5;
$val{"six"} = 6;
$val{"seven"} = 7;
$val{"eight"} = 8;
$val{"nine"} = 9;

# try looking up a digit name given a number
print "Enter a number 0-9\n";
$num = <STDIN>;
chop($num);
print "The name is " . $text{$num} . "\n";

# try looking up a numeric value given a digit name
print "Enter the name of a number zero - nine\n";
$name =  <STDIN>;
chop($name);
print "The value of $name is " . $val{$name} . "\n";

Associative Array Constants

You can create an entire associative array using an array constant, in this case perl treats the first array element as a key and the next as the corresponding value, then does the same with the remaining array elements in order. For example, the following definition of an associative array from an array constant:

%text = (0,"zero", 1, "one", 2, "two", 3, "three", 4, "four");
is shorthand for this:
$text{0} = "zero";
$text{1} = "one";
$text{2} = "two";
$text{3} = "three";
$text{4} = "four";

You can initialize an associative array from any array (including from an array variable) in the same way. You can also create (or redefine) an array variable from an associative array - in this case the translation is reversed. The resulting array will be a list of the keys and corresponding values. You have no control over the order of key,value pairs that are put in the array (perl uses an internal ordering for associative arrays that make the implementation efficient). An example:

# create an associative array from an array constant.
%text = ("Bill",45,"Nancy",23,"John",23,"Mary",65);

# now copy this to an array variable
@somearray = %text;

# now print out this array and notice the order is different
# than the original constant array

print "Array is @somearry\n";

# The result I got was the following output:
# Array is Mary 65 Nancy 23 Bill 45 John 33

There is an alternate syntax for creating associative array literals using the => operator:

%text = (0 => "zero", 
	 1 => "one",
         2 => "two", 
         3 => "three",
         4 => "four");
is shorthand for this:
$text{0} = "zero";
$text{1} = "one";
$text{2} = "two";
$text{3} = "three";
$text{4} = "four";

and this:

%text = (zero =>0, 
	 one => 1,
         two => 2, 
         "three" => 3,
         "four" => 4);
is shorthand for this:
$val{"zero"} = 0;
$val{"one"} = 1;
$val{"two"} = 2;
$val{"three"} = 3;
$val{"four"} = 4;

Notice that when using the => operator the key value does not need to be quoted (unless it contains whitespace).

No such key

If you attempt to access an element of an associative array using a key that does not exist in the assoc. array - the result is the special perl value undef.

Associative Array Operators

Perl supports some associative array operators including:

The keys operator extracts all the keys from an associative array and creates an array holding all the scalar value keys. The order of the keys in the resulting array is controlled by perl, so you shouldn't make any assumptions about the order. The values operator gets an array made up of all the values in the associative array (just the values - none of the keys). An example:

# create an associative array
$price{"Boxer"} = 45000;
$price{"Beetle"} = 18000;
$price{"MonsterSUV"} = 40000;
$price{"MiniVan"} = 25000;

@carnames = keys(%price);
# @carnames now is something like (order could be different):
#  ("Boxer", "Beetle", "MonsterSUV", "MiniVan")


@carprices = values(%price);
# @carprices now is something like (order could be different):
# (45000, 18000, 40000, 25000)

The delete operator deletes a single key/value pair from an associative array. This is the only way to shrink an associative array. An example:

# create an associative array
$price{"Boxer"} = 45000;
$price{"Beetle"} = 18000;
$price{"MonsterSUV"} = 40000;
$price{"MiniVan"} = 25000;


# now delete the record for Beetle (it's not worthy of inclusion!)
delete $price{"Beetle"};

@carnames = keys(%price);
# @carnames now is something like (order could be different):
#  ("Boxer", "MonsterSUV", "MiniVan")

Variable Interpolation and Associative Arrays

There is no variable interpolation in doubly quoted strings for an entire associative array (I'm not sure what to expect anyway), but you can use individual elements of an associative array. For example:

$text{1} = "one";
$text{2} = "two";
$text{3} = "three";
$text{4} = "four";

$i = 2;

print "The name of $i is $text{$i}\n";

print "The name of 3 is $text{3}\n";

Exercises

1. Write some Perl code that will create an HTML table, where the contents of the cells come from a Perl Array.

One possible solution:

@vals=("one","two","three");
# assume the array @vals has the values
print "<TABLE><TR>\n";
for ($i=0;$i<=$#vals;$i++) {
    print "<TD>$vals[$i]</TD>\n";
}
print "</TR></TABLE>";

Output Produced:

<TABLE><TR>
<TD>one</TD>
<TD>two</TD>
<TD>three</TD>
</TR></TABLE>

2. Print a paragraph tag where the attribute names and values are specified in a Perl hash (associative array).

One possible solution:

%attribs = (
    "ID" => "joe",
    "style" => "color:blue;",
    "onClick" => "check();"
);

printf("<P ");
foreach $attr (keys %attribs) {
    print "$attr=\"$attribs{$attr}\" ";
}
print ">";

Output Produced:

<P style="color:blue;" onClick="check();" ID="joe" >

NOTE: The code from both exercises would be more useful if put inside a subroutine (but we haven't covered subroutines yet!).