Lists and Array Variables

Perl also enables you to define an ordered collection of values, known as lists, this collection of values can be stored in variables known as array variables.

The array variable name can be any length but it has the following rules

Introducing Lists

A list is a sequence of scalar values enclosed in parentheses

Example Lists

(1, 5.3, "hello", 2)

(1, 5.3, $var)                 # once the element has been assigned the value of $var it remains this
                               # value even if you change $var

( 1, $var1 + $var2)            # 2nd element becomes the value of $var1 + $var2

(1, "This is string", 3)       # you can even use strings as values

Perl enables you to store lists in special variables designed for this purpose, these variables are called array variables.

Example Array Variables

@array = (1, 2, 3, 4, 5);      # here the array variable array contains the list 1, 2, 3, 4, and 5

Note: when the perl interpreter sees a @(at) sign it knows that it is an array.          

The arrays storage units are called elements and you can refer to any element within the array as if it was a scalar value, however when you access a single element the array name must start with a dollar ($) as you are accessing a single scalar value. Elements begin at zero, so when you access the first element it would be $array[0].

Array elements

$scalar = $array[0];           # The first element in the array
$scalar = $array[5];           # The 6th element in the array (remember its starts at zero)

$scalar = $array[$index];      # you can use the value of a variable to access the elements

Here is a complete array example

Simple array Example

@food = qw(apple banana orange lemon melon peach plum);   # array food will contain 7 elements (0-6)

$count = 1;
while ($count <= 7)
{
   print ("element $count is $food[$count-1]\n");         # using value of a variable to get element
   $count++;
}

Using List Ranges

Suppose you want to define a list of numbers from 1 to 10, you can use the list-range operator two consecutive periods (..) which makes it simpler.

List-Range Example

@array = qw(1 2 3 4 5 6 7 8 9 10);       # The old way   

@array = qw(1 .. 10);                    # the simpler way
@array = qw(2, 5 .. 7, 8);               # array contains 2, 5, 6, 7 and 8

@array = qw(3..3);                       # will contain one element 3

@array = qw($var1+10 .. $var2+100);      # can use complex expressions to create list ranges

@days_of_month("01".."31");              # list contains days of the month (including leading zeros)

@array = qw( 1 @list 3);                 # you can even use other arrays to populate arrays

Assignment and Array Variables

You can copy one array to another

Copy an Array

@food = qw(apple banana orange lemon melon peach plum);

@food_allergies = @food;

while ($count <= 15)
{
   print ("I would be very sick if i ate the following food $food_allergies[$count-1]\n");
   $count++;
}

Substituting array variables in Strings @num = (1, 2, 3);
print(@num, "\n");      # no spaces between numbers when printed: 123
print("@num\n");        # puts a space between array elements: 1 2 3
Assigning scalar variables from arrays

@burgers = ("Big Mac's", "Whopper's");

($mcdonalds, $burger_king) = @burgers;         # assign the array elements to the scalar variables

print("Mcdonalds have $mcdonalds\n");
print("Burger King have $burger_king\n");

($dummy1, $dummy2, $dummy3) = @burgers;        # $dummy3 will have null assigned as not enough elements

Get the length of an array

@food = qw(apple banana orange lemon melon peach plum);

$number_of_food_elements = @food;              # get the number of elements in array food
print ("There are $number_of_food_elements elements in array food\n");

$count = 1;
## Use the scalar variable for the element value
while ($count <= $number_of_food_elements)     # use the number of elements in the while loop (could use
{                                              # @food as well)
   print ("$food[$count-1]\n");
   $count++;
}

## Use the array itself to get the number of elements
while ($count <= @food)                        # same effect as above
{                                              
   print ("$food[$count-1]\n");
   $count++;
}

Array Slices @food = qw(apple banana orange lemon melon peach plum);

@part_of_food = @food[2,3,4];                  # array will have values orange, lemon and melon

@part_of_food = @food[2 ..4];                  # same as above using range-list

$maximum = @food;                              # obtain number of elements in food array
@part_of_food = @food[2 .. $maximum];          # same as above using range-list and scalar variables
Array Slices assigning @food = qw(apple banana orange lemon melon peach plum);

@food[@food .. @food+3] = qw(pineapple strawberry blackberry raspberry); #add to array food

$count = 1;
while ($count <= @food)
{
   print ("$food[$count-1]\n");
   $count++;
}

Overlapping slices @food = qw(apple banana orange lemon melon peach plum);

@food[1,2,3] = @food[4,5,6];    # we overwrite existing elements using same array food

$count = 1;
while ($count <= @food)
{
  print ("$food[$count-1]\n");
  $count++;
}

Reading an Array from standard input file @input_file = <STDIN>;

$count = 1;
while ($count <= @input_file)
{
   print ("$input_file[$count-1]\n");
   $count++;
}

Array Library Functions

Perl provides a number of library functions that work on lists and array variables. You can use them to do the following

Sort an array

@array = ("this", "is", "a", "test");

@array2 = sort (@array);                # array2 will contain ("a", "is", "test", "this");

@num = (70, 100, 8);
@num2 = sort (@num);                    # num2 will contain ( 100, 70, 8 ) not (8, 70, 100) - see below

Note: sort treats numbers as strings

Reverse the elements

@array = ("backwards", "is", "array", "this");

@array2 = reverse @array;               # array2 will contain ("this", "array", "is", "backwards");

Remove the last character from all elements @list = ("rabbit", "12345", "quartz");
chop (@list);                           # list will now contain ("rabbi", "1234", "quart");
Merge Elements

@list = ("here, "is", "a");

$string = join (" ", @list, "String");  # $string will contain " here is a String", whitespace is the join character                    
$string = join ("::", @list, "String"); # $string will contain "::here::is::a::String" :: is the join character

Note: the first argument to join is the join character   

Split a string into array elements $string = "Hello:what:a:nice:day";

@array = split(/:/, $string);           # array will contain ("Hello", "what", "a", "nice", "day");

Associative Arrays

Associative arrays use scalar values as subscripts, which means you can have a word represent the index value. Perl uses the percent sign (%) to distinguish an associative array from an ordinary array variable.

Referring to Associative array elements

$fruit{"bananas"} = 1;
$number{3.14159};
$integer{-7};
$fruit{$my_fruit};

Note: using the dollar sign ($) tells the perl interpreter that this is a single scalar item and is to be treated as such

Creating an associative array

%associative_array;                          # declare an associative array
%array = ("foo", "26", "bar", "27");         # declare and populate an associative array

$fruit{"bananas"} = 1;
$fruit{"apples"} = 3;
foreach $item (sort keys (%fruit)) {
   print $item . " = " . $fruit{$item} . "\n";
}

Note: There is no order within the array, elements can be all over the place

Copying associative arrays from array variables @fruit = (apples", 6, "cherries", 8, "oranges", 11);
%fruit = @fruit;
Adding an element $fruit{"bananas"} = 1;
Deleting an element

delete($fruit{"bananas"});

Note: delete is the only way to delete from an associative array

Listing the keys

%array = ("foo", "26", "bar", "27");
@keys = keys(%array);
print("@keys\n");

# keys is commonly used as below
foreach $i (keys (%array)) {
   print $i . "\n";
}

Note: in no particular order will the list be returned

Listing the values %array = ("foo", "26", "bar", "27");
@values = values(%array);
print("@values\n");

foreach $i (values (%array)) {
   print $i . "\n";
}

Note: in no particular order will the list be returned

Looping an associative array

%valle = ("Paul", 35, "Lorraine", "31", "Dominic", 12, "Jessica", 8);

## Using the foreach loop
foreach $i (keys (%valle)) {
   print $i . " " . $valle{$i} . "\n";
}

# Using a while loop
while (($name, $age) = each(%valle)) {
   print $name . " " . $age . "\n";
   ## delete $valle{"Paul"};               ## Do not use delete as it makes each unpredictable
}

Note: do not use delete when using each, because the behavior of each unpredictable

For more on manipulating arrays see List Manipulation.

Data Structures

You can use associative arrays to simulate a wide variety of data structures found in high-level languages, you could implement the following data structures

Linked Lists

A Linked List is a simple data structure that enables you to store items in a particular order. Each element of the linked list contains two fields

Linked List structure %words = ("alpha", "bravo",
          "bravo", "Charlie",
          "Charlie", "delta",
          "delta", "");

$header = "alpha"
Complete Linked List Example # initialize list to empty
$header = "";                                 
while ($line = <STDIN>) {
   # remove leading and trailing spaces
   $line =~ s/^\s+|\s+$//g;
   @words = split(/\s+/, $line);
   foreach $word (@words) {
      # remove closing punctuation, if any
      $word =~ s/[.,;:-]$//;
      # convert all words to lower case
      $word =~ tr/A-Z/a-z/;
      &add_word_to_list($word);
   }
}

&print_list;

sub add_word_to_list {
   local($word) = @_;
   local($pointer);

   # if list is empty, add first item
   if ($header eq "") {
      $header = $word;
      $wordlist{$word} = "";
      return;
   }

   # if word identical to first element in list, do nothing
   return if ($header eq $word);

   # see whether word should be the new first word in the list
   if ($header gt $word) {
      $wordlist{$word} = $header;
      $header = $word;
      return;
   }

   # find place where word belongs
   $pointer = $header;
   while ($wordlist{$pointer} ne "" && $wordlist{$pointer} lt $word) {
      $pointer = $wordlist{$pointer};
   }

   # if word already seen, do nothing
   return if ($word eq $wordlist{$pointer});
   $wordlist{$word} = $wordlist{$pointer};
   $wordlist{$pointer} = $word;
}

sub print_list {
   local ($pointer);

   print ("Words in this file:\n");
   $pointer = $header;
   while ($pointer ne "") {
      print ("$pointer\n");
      $pointer = $wordlist{$pointer};
   }
}

Structures

Many programming languages enable you to define collections of data called structures. Like Lists, structures are collections of values, each element of a structure, however, has its own name and can be accessed by that name.

Simulate a structure

# C Structure
struct {
   int field1;
   int field2;
   int field3;
} mystructure;

# Perl Simulated structure
%mystructure = ( "field1", "",
                 "field2", "",
                 "field3", "");

Trees

A tree is similar to a linked list, except that each element of a tree points to more than one other element. The simplest example of a tree is a binary tree. Each element of a binary tree, called a node, points to two other elements, called the left child and right child. Each of these children points to two children of its own and so on, the very last node are called leaf nodes.

The following terminology is used when describing trees

Tree Example $rootname = "parent";
%tree = ("parentleft", "child1",
         "parentright", "child2",
         "child1left", "grandchild1",
         "child1right", "grandchild2",
         "child2left", "grandchild3",
         "child2right", "grandchild4");
# traverse tree, printing its elements
&print_tree($rootname);

sub print_tree {
   local ($nodename) = @_;
   local ($leftchildname, $rightchildname);

   $leftchildname = $nodename . "left";
   $rightchildname = $nodename . "right";
   if ($tree{$leftchildname} ne "") {
      &print_tree($tree{$leftchildname});
   }
   print ("$nodename\n");
   if ($tree{$rightchildname} ne "") {
      &print_tree($tree{$rightchildname});
   }
}

Databases

Using the above methods you can build quite complex databases, associative arrays can use variable records lengths, are accessible either sequentially or randomly.

The below calculator example is a very complex script, it uses associative arrays and recursive subroutines and the tree structure.

Calculator Example:

To run the calculator program

$calculator
11 + 5 * (4 - 3)
^D

Should result in 16

very complex example
# statements which initialize the program
$nextnodenum = 1;  # initialize node name generator
&get_next_item;    # read first value from file

$treeroot = &build_expr;
$result = &get_result ($treeroot);
print ("the result is $result\n");

# Build an expression.
sub build_expr {
        local ($currnode, $leftchild, $rightchild);
        local ($operator);

        $leftchild = &build_add_operand;
        if (&is_next_item("+") || &is_next_item("-")) {
                $operator = &get_next_item;
                $rightchild = &build_expr;
                $currnode = &get_new_node ($operator,
                        $leftchild, $rightchild);
       } else {
                $currnode = $leftchild;
       }
}

# Build an operand for a + or - operator.
sub build_add_operand {
        local ($currnode, $leftchild, $rightchild);
        local ($operator);

        $leftchild = &build_mult_operand;
        if (&is_next_item("*") || &is_next_item("/")) {
                $operator = &get_next_item;
                $rightchild = &build_add_operand;
                $currnode = &get_new_node ($operator,
                        $leftchild, $rightchild);
       } else {
                $currnode = $leftchild;
       }
}

# Build an operand for the * or / operator.
sub build_mult_operand {
        local ($currnode);

        if (&is_next_item("(")) {
                # handle parentheses
                &get_next_item;  # get rid of "("
                $currnode = &build_expr;
                if (! &is_next_item(")")) {
                        die ("Invalid expression");
                }
                &get_next_item;  # get rid of ")"
        } else {
                $currnode = &get_new_node(&get_next_item,
                            "", "");
       }
       $currnode;  # ensure correct return value
}

# Check whether the last item read matches
# a particular operator.
sub is_next_item {
        local ($expected) = @_;

        $curritem eq $expected;
}

# Return the last item read; read another item.
sub get_next_item {
        local ($retitem);

        $retitem = $curritem;
        $curritem = &read_item;
        $retitem;
}

# This routine actually handles reading from the standard
# input file.
sub read_item {
        local ($line);

        if ($curritem eq "EOF") {
                # we are already at end of file; do nothing
                return;
        }
        while ($wordsread == @words) {
                $line = <STDIN>;
                if ($line eq "") {
                        $curritem = "EOF";
                        return;
                }
				# the following two lines fix a problem
				# in the example as printed in the book
				$line =~ s/\(/ ( /g;
				$line =~ s/\)/ ) /g;
               $line =~ s/^\s+|\s+$//g;
               @words = split(/\s+/, $line);
               $wordsread = 0;
        }
        $curritem = $words[$wordsread++];
}

# Create a tree node.
sub get_new_node {
        local ($value, $leftchild, $rightchild) = @_;
        local ($nodenum);

        $nodenum = $nextnodenum++;
        $tree{$nodenum} = $value;
        $tree{$nodenum . "left"} = $leftchild;
        $tree{$nodenum . "right"} = $rightchild;
        $nodenum;   # return value
}

# Calculate the result.
sub get_result {
        local ($node) = @_;
        local ($nodevalue, $result);

        $nodevalue = $tree{$node};
        if ($nodevalue eq "") {
                die ("Bad tree");
        } elsif ($nodevalue eq "+") {
              $result = &get_result($tree{$node . "left"}) +
                     &get_result($tree{$node . "right"});
        } elsif ($nodevalue eq "-") {
              $result = &get_result($tree{$node . "left"}) -
                     &get_result($tree{$node . "right"});
        } elsif ($nodevalue eq "*") {
              $result = &get_result($tree{$node . "left"}) *
                     &get_result($tree{$node . "right"});
        } elsif ($nodevalue eq "/") {
              $result = &get_result($tree{$node . "left"}) /
                     &get_result($tree{$node . "right"});
        } elsif ($nodevalue =~ /^[0-9]+$/) {
                $result = $nodevalue;
        } else {
                die ("Bad tree");
        }
}

List-Manipulation Functions

There are two groups of functions that manipulate lists and arrays

Array and List Functions

The following functions manipulate standard array variables and the lists that they store

grep extract the elements of a list that match a specified pattern
splice enables you to modify the list stored in an array variable, by passing appropriate arguments to splice, you can add elements to the middle of a list, delete portion of a list, or replace a portion of a list.
shift remove an item from the beginning a list
unshift put an item at the beginning of a list
push put an item to the end of a list
pop remove an item from the end of a list
split split a character string into a list of elements
sort/reverse sort a list alphabetically
reverse reverse the order of a list
map enables you to use each element of a list, in turn as an operand in an expression
wantarray wantarray determines if the calling function wants a scalar or array variable returned
Examples
grep

# grep (<pattern>, <searchlist>)

$line = "This line of input contains 8, 11 and 26.";
@words = split(/\s+/, $line);

@numbers = grep(/^\d+[.,;:]?$/, @words);
print ("Numbers: @numbers");

Note: you can also use grep with the file-test operators

opendir(CURRDIR, ".") || die("Can't open current directory");
@filelist = grep(!/^\./, grep(-r, readdir(CURRDIR)));
print ("@filelist");

splice

# retval = splice(<array>, <skipelements>, <length>, <newlist>);

# Replace
@array = qw(1 2 3 4);
splice (@array, 1, 2, ("two", "three"));        # list will now be (1 two three 4);

# Appending
splice(@array, 3, 0, "hello", "there");         # add to position 3 shifting anythnig after
splice(@array, @array, 0, "hello", "there");    # add to the end of the list

# Deleting
splice(@array, 2, 2);                           # delete two elements from the offset

shift/unshift

## add or delete from the left side of the array, element 0

@array = qw(1 2 3 4);

$first_element = shift(@array);

print ("Array: @array");
print("\nFirst element:" . $first_element);

unshift(@array, $first_element);
print ("\nArray: @array");

push/pop

## add or delete from the right side of the array, last element

@array = qw(1 2 3 4);

$first_element = pop(@array);

print ("Array: @array");
print("\nFirst element:" . $first_element);

push(@array, $first_element);
print ("\nArray: @array");

split

$string = "one::two::three::four::five::six";
@words = split(/::/, $string);
@limit_words = split(/::/, $string, 3);         # specify a maximum number of elements
print ("@words");
print ("@limit_words");

sort @array = qw( one two three four five six);

@sorted = sort(@array);
print ("@sorted\n");

reverse @array = qw( one two three four five six);

@reversed = reverse(@array);
print("@reversed");

map # map(<expression>, <array>);        # The map function uses the system variable $_ for each element

@array = qw(100 200 300 400);

@results = map($_+1, @array);

print("@results");

wantarray @array = &mysub();                   # using an array the wantarray will return true
$scalar = &mysub();                  # using an scalar the wantarray will return false

sub mysub {
   if (wantarray()) {
      print("Returning type is an array - TRUE\n");
   } else {
      print("Returning type is an scalar - FALSE\n");
   }
}

Here are some equivalent comparisions using the splice command assuming <CODE class=inline><SPAN class=i>($[</SPAN> == <SPAN class=n>0</SPAN> and <SPAN class=i>$#a</SPAN> >= <SPAN class=i>$i</SPAN></CODE> )

Add an item on the end of a list push(@a,$x) splice(@a,@a,0,$x)
Remove an item from the end of a list pop(@a)     splice(@a,-1)
Remove an item from the beginning of a list shift(@a) splice(@a,0,1)
Add an item to the beginning of a list unshift(@a,$x,$y) splice(@a,0,0,$x,$y)
set a element array to a value $a[$x] = $y splice(@a,$x,1,$y)

To create a queue you would use push and shift, and to create stack you would use push and pop.

Associative Array Functions

The following functions manipulate associative arrays

keys returns a list of subscripts of the element of an associative array
values returns a list consisting of all the values in an associate array
each returns an associative element as a two element list
delete deletes an associative array element
exists enables you to determine whether a particular element of an associative array exists.
Examples
keys

%array = ("foo", "26", "bar", "27");
@keys = keys(%array);
print("@keys\n");

# keys is commonly used as below
foreach $i (keys (%array)) {
   print $i . "\n";
}

Note: in no particular order will the list be returned

values %array = ("foo", "26", "bar", "27");
@values = values(%array);
print("@values\n");

foreach $i (values (%array)) {
   print $i . "\n";
}

Note: in no particular order will the list be returned

each %array = ("foo", "26", "bar", "27");
@each = each(%array);
print("@each\n");

foreach $i (each (%array)) {
   print $i . "\n";
}

Note: in no particular order will the list be returned, also do not use delete when using each, because the behavior of each unpredictable

delete %array = ("foo", "26", "bar", "27");

$retval = delete($array{"foo"});        # returns the deleted elements value 26

foreach $i (keys (%array)) {
   print $i . "\n";                     # only bar should exist
}

print $retval;         

exists %array = ("foo", "26", "bar", "27");

if ( exists($array{"foo"}) ) {
   print("Foo exists");
} else {
   print ("Foo does NOT exists");
}

When using associative arrays do not use push, pop, shift or splice because the position of any particular element in the array is not guaranteed.