LuxSci

Introduction to Internet Programming: Part 5 – Perl Data Part I – Lists

Published: February 13th, 2010

[RAW]

This article is Part 5 of our series on Internet Programming. See the Introduction and Part 1, Introduction to HTML. This article covers the Perl “list” data type.
[/RAW]

[RAW]

5.1 Motivation

Using “scalar” variables we have been able to store any single
datum into a named box — strings, numbers, dates, boolean values, etc.
These add a great versatility and power that makes programming at all useful.
However, there are many instances where you need to store lots of information
in lots of variables and the fact that each variable has a different name
makes it difficult to simplify your program with loops and other devices.

For example, in the homework for lesson 3, you had to write a MadLib
that would ask the user for 8 different words with 8 different prompts. You
might have written something like this:

print "Enter a noun:";
$word1 = <STDIN>;
chomp($word1);

print "Enter an action verb:";
$word2 = <STDIN>;
chomp($word2);

And so on until you had asked 8 questions. This works fine, but seems pretty
redundant as you are essentially doing the exact same thing eight times in a row,
with only the contents of the prompt and the name of the variable are changing. This
is where Perl’s “list” variable type comes in handy. A list is a named variable
with numbered “compartments” where you can store data. Thus a list
is a numbered collection of variables of the (scalar) type we have been using so
far.

5.2 Making a List all at Once

Let’s demonstrate the one syntax for making a list in Perl, then explain all
the nuances:

@prompts = ( "Enter a noun",          "Enter an action verb",
             "Enter a person's name", "Enter a place",
             "Enter an action verb",  "Enter a woman's name",
             "Enter an occupation",   "Enter a type of animal" );

Here we have made a list to contain the prompts for the eight MadLib entries
we need. Starting from the left, the name of the list is prefixed with an
at-sign (“@“). Perl knows that you mean a list and not a regular variable
because you have the at-sign there and not the dollar sign. The names of lists follow
all the same rules as names for other variables (with the exception of the prefix).
One interesting thing to note is that the names of lists are independent of the
names for regular (scalar) variables, So it you have a list @x and a
normal variable $x, then they will be completely different, non-ambiguous
and independent variables! It is, however, not recommended to use the same name for different
types of variables as it can lead to confusion on the part of the programmer
and inadvertent bugs.

This list is formed as a comma-delimited set of items inside parenthesis. The individual
items can be strings, numbers, the results from an expression, etc. Since spaces don’t
matter outside quotation marks in Perl, we use them to line up our list elements to make
our program more readable — not necessary, but nice.

5.3 Storing and Retrieving Individual Items From a List

To retrieve a value from a list, we need to know what index in the list the item
is located at. Lists created as in the example above start at index ZERO, so the first
item is list element zero an the last one is list element seven. To print the value of
list element 2, we would

print $list[2];    # Prints the string "Enter a person's name"

This prints the list element at index “2” which is the third item in the list.
The syntax may seem a little odd… why is there a $ prefix there is we are dealing
with a list? Well, lets start from the other side. You specify the list element you
want by tacking on a suffix to the name of the list of the form “[index]“,
where index can be an integer number, like “[2]“, or a number stored in a
variable, like “[$index]“, or the result of a function call, like
[sqrt(4)]“. Now, as to why the prefix is suddenly a dollar sign, think about
what we are doing — we had a list of items and the list uses a prefix @; we are
getting back a single item (and not a list) which is essentially a regular
variable (because that is how it behaves). Since each item in your list is a regular
variable, you refer to them using the dollar sign! (If you used the at sign prefix
accidently, you would usually still be OK, but it has a slightly different meaning that may
cause you problems in certain circumstances, so use the dollar sign…)

So, you can retrieve an element from a list and use it
wherever you could use
a normal variable. This includes assignment. I.e.


@list = ("a","b","c","d");

print "$list[0]\n";

$list[1] = "$list[0] - 123 - $list[1]";

$i = 2;

if ($list[$i] eq "c") {
   print "It's still 'c'!\n";
   }

Some quick facts about lists:

  • Use list items just like you use normal variables
  • If you assign something to an index of a list that has never been used,
    then the list will automatically make room for it, so “$list[1000] = 33.4;
    is OK, even if our list only had 4 elements in it before! All the elements from
    index 4 to 999 are still “undefined”, but available for use.

  • If you try to retrieve an element whose index isn’t in the list,
    or whose value has not yet been set, i.e. $list[999],
    this is the same as trying to retrieve a value from a regular variable that you
    have never used. Numerically, the result is treated as ZERO; lexically, it is treated
    as an empty string. If you want to see IF the value has not been defined, use
    the undef keyword in a lexical comparison:

       if ($list[500] eq undef) { print "Not Defined"; }
       if ($list[500] ne undef) { print "Defined!"; }
       
  • You can have as many items in a list as you want. You never have to worry
    about managing the memory yourself.

  • You don’t have to initialize a list with an assignment
    like “@list = (1,2,3);
    — you can just start assigning values using the “$list[$i] = 3;
    type of assignment syntax.

  • If you have a list and you want to empty it, assign an empty list to it:
            @list = ();  ## Makes the list empty
            

5.4 Some Simple List Examples

Let’s complete the MadLib using lists:

@prompts = ( "Enter a noun",          "Enter an action verb",
             "Enter a person's name", "Enter a place",
             "Enter an action verb",  "Enter a woman's name",
             "Enter an occupation",   "Enter a type of animal" );

@answers = ();

## Get their answers for all questions

for ($index = 0; $index < 8; $index = $index + 1) {

   ## Loop until they type something in (and don't just
   ## hit Enter

   do {
      print "$prompts[$index]: ";  # Display prompt
      $answers[$index] = <STDIN>;
      chomp( $answers[$index] );
      } while ($answers[$index] ne "" );

   }

## display their answers back -- You could replace
## this look with the actual display of the MadLib
## text.

for ($index = 0; $index < 8; $index = $index + 1) {
   print "Answer $index is '$answers[$index]'\n";
   }

Finding the Length: Very often you want to find the length of a list (i.e.
the number of variables in it), or a scalar (i.e. the number of characters in it).
For regular variables (scalars), you can use the length command. For lists,
you use a special syntax that returns the index of the last item in the list.
So, for @list, $#list is the "index of the last element in the list.
This is "0" if the list has 1 element, "5" if the list has six elements and "-1" if the
list has zero elements. So, the number of items in any list is found by adding one
to this number, i.e. "$#list + 1":

$input = <STDIN>;    ## Assume the user types "Joe"
                                ## then presses [Enter]
print $input;                   ## Prints "Joe\n"
print length($input), "\n";     ## Prints 4 (the "\n" is 1 character,
                                ##    a linefeed)

chomp();                  ## Delete all linefeeds
print length($input), "\n";     ## Prints 3

@list = ("a","b","c","d");
$length = $#list+1;               ## $length = 4
for ($i=0; $i<$length; $i++) {
   print "$list[$i]\n";
   }

print $list[ $#list ], "\n";    ## Print the last list item.

A New Loop: Since one of the most common uses of lists is
to iterate over the elements, Perl has a special loop to do just that.
It is a version of the for loop:

@list = ("a","b","c","d");
for $item (@list) {
    print "$item\n";
    }

This type of for loop iterates over all items in your list,
@list, and assignees the value of each item to $item
in turn, then executes the statements inside the curly brackets. In this
way, you do not need to know the length of the list, keep track of the current
position in the list, or use the [index] notation to read the list's
elements. The disadvantages of this type of loop are: 1. Assigning or changing
the value of the variable $item will not affect the value actually stored
in the list itself, as $item only contains a copy of that information,
and 2. you don't know what index in the list you are at, unless you are keeping a separate
count, which would defeat much of the benefit of using this form of loop, anyway.
We will see more examples of this type of loop below.

5.5 Special Functions for Lists

Perl has many functions designed just for manipulating your lists. Here we shall
discuss a few of the more useful and ubiquitous ones.

push: The push function allows you to add an element to
the end of a list (to "push" an element onto the end). For example, if you
have a list of 5 elements and you push a new one on to list, it will become the 6th
element (at index 5), i.e.

@list = (3.1415, 1.414, 2.333);  # No quotes needed for numbers.

push(@list, 77);

print $list[3];    # Prints "77"

The syntax of push is that it takes 2 arguments separated by a
comma (actually, this is a list of arguments, but we'll get back to that in
a later lesson). The first argument is the list you want to push on to and the
second argument is what is pushed.

pop: The pop function is the opposite of push.
It removes the last item from the list and returns it to you. After you
use pop, your list is smaller. If your list is already empty and you use
pop, it will return a value of undefined (undef).

@list = (3.1415, 1.414, 2.333);  # No quotes needed for numbers.

$x = pop(@list);  # Remove and return the last item

print "$x\n";     # Prints "2.333\n"
print "$list[2]";   # Prints "" since there is nothing there!

shift: The shift function is like the pop command,
but instead of removing the last element from the list, it removes the first element in the
list and shifts all the other elements down one index. It then returns to you
that element that was removed. If your list is already empty and you use
shift, it will return a value of undefined (undef).

@list = (3.1415, 1.414, 2.333);  # No quotes needed for numbers.

$x = shift(@list);  # Remove and return the first item

print "$x\n";     # Prints "3.1415\n"
print "$list[2]";   # Prints "" since there is nothing there!
print "$list[0]";   # Prints "1.414" since this is the new 1st item.

join: The join command does not alter a list like
those above; it allows you to make one long string out of all the elements
in your list. You give join a list and a delimiter string. join
returns to you a string containing all elements of your list, where each
element is separated from the next with a copy of your delimiter string. I.e.

@list = (3.1415, 1.414, 2.333);  # No quotes needed for numbers.
$x = join(", ", @list);
print $x;               # Prints "3.1415, 1.414, 2.333"
print join("|",@list);  # Prints "3.1415|1.414|2.333"
print join(".",@list);  # Prints "3.1415.1.414.2.333"

Clearly, join is very useful when you want to convert a list into
a string for display or for storage in a normal variable. However, if you want to be able
to convert your string back to a list later, be sure that the delimiter you use cannot
be confused with any of the text that is in the list elements; i.e. see the last print
statement above where we join the elements together with a period. In the output,
there are more periods than delimiters and it is ambiguous where one item begins and ends.
Choose you delimiters wisely.

split: The split command is the opposite of the join
command. It takes a delimiter and a string and returns to you a list containing all of
the pieces of the string between the delimiters. If there are no matching delimiters in
the string, then you get a list containing only 1 item -- the original string!

@list = ();     ## An empty List
print join(", ",@list); ## Prints "" -- the list is empty!

$x = "3.1415|1.414|2.333";
@list = split("|",$x);

print join(", ",@list); ## Prints "3.1415, 1.414, 2.333"
print $list[2];         ## Prints "2.333"

@list = split("--",$x);  ## There is no "--" in $x!
print join(", ",@list);   ## Prints "3.1415|1.414|2.333"
print $list[0];           ## Prints "3.1415|1.414|2.333"
print $list[1];           ## Prints "" -- the list has only 1 elet.

5.6 Useful Examples

A list within a list using split and join. Hint -- look
at this when doing your homework!

# A list of people and information about them
# Each person is one item in the list
# The information about them is in a "|" delimited string...

@info = ( "John Smith|678-564-2889|28",
          "Karen Green|599-234-1234|19",
          "Jake Hagerson|890-111-0352|22" );

for ($i=0; $i <= $#info; $i++) {
   @data = split("|",$info[i]);  # Get the individual info.
   print "Name:   $data[0]\n";
   print "Phone:  $data[1]\n";
   print "Age:    $data[2]\n\n";
   }

Creating an array from user input:

print "Enter a series of numbers, one per line.\n";
print "Enter 'exit' when you are done.\n\n";

@numbers = ();    ## Empty array

do {                         ## Do loops perform the statements

   print "Number (or 'exit'):";

   $input = <STDIN>;         ## FIRST, then check the condition.
   chomp($input);

   if (lc($input) eq "exit") {  # Exit the loop
      last;
      }

   push(@numbers, $input);  ## Add to the list!

   } while (1==1);    ## Always TRUE


if ($#numbers + 1 == 0) {       ## No Numbers?
   print "No numbers entered. Exiting.\n";
   exit;
   }

## Calculate the average

$sum = 0;
for $n (@numbers) { $sum = $sum + $n; }
$average = $sum / ($#numbers + 1);

print "The average is $average\n";

## Calculate the standard deviation

if ($#numbers == 0) {       ## Only 1 number?
   print "Not Enough information to calculate a\n";
   print "standard deviation\n\n";
   exit;
   }

$stddev = 0;
for $n (@numbers) {
   $stddev = $stddev + ($n - $average) ** 2;
   }
$stddev = sqrt($stddev / $#numbers);

print "The Standard Deviation is ";
print "$stddev\n";

5.7 Your Homework

For today's homework, you are going to generalize homework assignment #1
from lesson 4 -- the multiple choice question. You must do the following:

  1. Have 2 or more multiple choice questions
  2. Store the questions in a list. (I.e. 2 questions will be a list containing
    2 strings, the statement of each question). Do not include the possible
    answers here. I.e. A question might be "On what day in December is Christmas
    celebrated?". See Section 5.2 for how to make this list.

  3. Store the answers to the questions in another list,
    where the possible answers that you will display to the user are
    delimited from each other by some character like "|". So,
    for the Christmas question, your answer string might be
    "December 12th|December 17th|December 25th|December 31st".
    (You can use split to convert this into a list for nice printing
    to the user. See the first example in Section 5.6)

  4. Store the correct answer to each question in another list. I.e. if we are asking
    these questions by letter, you might use the strings in #3 and #2 to display something like:

    On what day in December is Christmas celebrated?
    A. December 12th
    B. December 17th
    C. December 25th
    D. December 31st
    Enter your answer:


    The answer would be "C".
    This would be the value stored in the appropriate item in the answer list.

  5. Have your script ask the user each of the questions in turn (read your question
    and answer lists in a loop, display the question, split and display the possible
    answers, get the user's answer and compare with the actual answer.)

  6. Don't tell the user if he/she got the answer right or wrong, just remember
    this fact by storing it in another list (think push).

  7. After all the questions have been asked and answered, tell the user which ones
    he/she got right and wrong and tell them what percentage they got correct.

[/RAW]

One Response to “Introduction to Internet Programming: Part 5 – Perl Data Part I – Lists”

  1. Introduction to Internet Programming Part 6: Perl Data Part II – Hashes | LuxSci FYI Says:

    […] you are already familiar with lists from Lesson 5, it is easiest to show you examples of using a hash and explain how it is different from how you […]

Leave a Comment


You must be connected or logged in to post a comment. This is to reduce spam comments.

If you have not previously commented, you can connect using existing social media account, or register with a new username and password.