CIS 24: CGI and Perl Programming for the Web

Class 3 (9/25) Lecture Notes

Topics

  1. Review of Last Week's Lab Assignment
  2. Equals (=), Equals (eq), and Equals (==)
  3. Lists and Array Variables
  4. Some Array Functions
  5. Using Arrays with the localtime Function
  6. Looping Through Arrays: for and foreach
  7. Controlling Loops
  8. Next Week's Quiz
  9. Lab: Exercises

Return to CIS 24 home page


  1. Review of Last Week's Lab Assignment
    1. Write a script that reads two lines of input from STDIN (Standard Input), and then prints both lines to the screen.
      print "\ntype something: ";
      $input1 = <STDIN>;
      chomp $input1;
      print "\ntype something else: ";
      chomp ($input2 = <STDIN>);
      print "This is the first thing you typed: $input1\n";
      print "This is the second thing you typed: $input2\n";

    2. Write a script that reads a line of input and prints a "1" if the line consists of a non-zero integer, and prints nothing if the line consists of 0 or a string. You must do this without using the conditional operators, if, unless, while, or until!
      print "Type a number or some text; you'll then see the number 1\n";
      print "printed to the screen if you typed a non-zero integer, or nothing\n";
      print "printed to the screen if you typed 0 or a string: ";
      chomp ($input = <STDIN>);
      print ($input > 0 || $input < 0);

    3. Write a script that converts miles to kilometers. There are 1.609 kilometers in a mile. The script should:
      • Read an integer from STDIN, which is the distance to be converted (e.g. the user could type in 10)
      • Convert the distance to kilometers (in this example, 16.09 km) and print it to the screen.
      print "Covert the following number of miles to kilometers: ";
      chomp ($mi = <STDIN>);
      $km = $mi * 1.609;
      print "$mi miles equals $km kilometers";

    4. Write a script that:
      1. Defines a variable called $password (you can give it whatever value you like)
      2. Prints a message to the screen asking the user to enter a password
      3. Uses standard input to accept what the user types in
      4. Prints a message to the screen indicating whether or not what they typed in matches $password
      $password = "potatohead";
      print "Please enter the password: ";
      chomp ($input = <STDIN>);
      
      if ($input eq $password) {
      	print "That's the correct password!\n";
      }
      
      else {
      	print "Sorry, that's not the right password.\n";
      }

    5. Write a script that:
      1. Sets the value of a variable called $count to 0
      2. Uses a while loop and the autoincrement operator to increment the value of $count to 5
      3. After each increment, prints a message to the screen indicating the current value of $count
      $count = 0;
      while ($count < 5) {
      	$count++;
      	print "the value of \$count is $count\n";
      }

    6. Write a script that:
      1. Prints to the screen "What is 17 plus 26?"
      2. Lets the user type in an answer via STDIN
      3. Uses an until loop to keep asking the user for an answer until he or she enters the correct answer
      print "What is 17 plus 26? ";
      chomp ($input = <STDIN>);
      
      until ($input == (17 + 26)) { 
      	print "Sorry, that's incorrect. Please try again: ";
      	chomp ($input = <STDIN>);
      }
      
      print "That's correct! 17 plus 26 equals ", (17 + 26), "\n";
      

    7. Write a script that:
      1. Asks the user to type in two different integers via STDIN
      2. Assigns each integer to a variable
      3. If the two numbers are equal, prints a message saying so
      4. If the first number is greater by one, prints a message saying so
      5. If the second number is greater by one, prints a message saying so
      6. If none of the above conditions are true, prints the message "The two numbers are not equal"
      print "Type in an integer: ";
      chomp ($input1 = <STDIN>);
      print "Type in another integer: ";
      chomp ($input2 = <STDIN>);
      
      if ($input1 == $input2) {
      	print "$input1 and $input2 are the same number.\n";
      }
      
      elsif ($input1 == ($input2 + 1)) {
      	print "$input1 is one more than $input2\n";
      }	
      
      elsif ($input2 == ($input1 + 1)) {
      	print "$input2 is one more than $input1\n";
      }
      
      else {
      	print "The two numbers are not equal.\n";
      }

  2. Equals (=), Equals (eq), and Equals (==)
  3. A point I emphasized in last week's class is the importance of using the right operator for your purpose. For example, don't use < when you mean lt. The former is a numeric comparison operator, and the latter is a string comparison operator. In the expression: "a" < "b", the operands are evaluated as numbers, since this is a numeric comparison operator. "a" and "b" have no value as numbers (they each evaluate to null). Since null is not less than null, the expression returns false. If you used lt instead of < the expression would return true, since "a" is less than "b" in a string context.

    The foregoing type of problem comes up most frequently when performing assignments and when testing for equality. So it's very important 1. to not mix up the assignment operator = with either of the equality comparison operators == and eq, and 2. to not mix up the equality comparison operators with each other. These are probably the most common mistakes in Perl programming, so I'd like to illustrate with a couple of examples:

    print "please type your name: ";
    $name = <STDIN>;
    chomp ($name);
    print ("hello") if ($name eq "mike");

    The above does a string comparison of $name with "mike". It will return true if they are the same string, and false if they are not. The code below does a numeric comparison of $name with "mike". Assuming the user types in a string, it will always return true, since both strings have a null value in a numeric context, and null equals null.

    print "please type your name: ";
    $name = <STDIN>;
    chomp ($name);
    print ("hello") if ($name == "mike");

    Here's an example of incorrect use of the assignment operator:

    print "please type your name: ";
    $name = <STDIN>;
    chomp ($name);
    print ("hello") if ($name = "mike");

    The last line of code assigns the string "mike" to $name. The assignment operation is successful, so it returns true. The if statement then prints the string "hello", regardless of what the user typed in.

    A good way to guard against these kinds of mistakes is to run Perl with warnings turned on, by using the -w switch. This will result in a warning message whenever Perl detects something like strings being used as operands with numeric operators.

  4. Lists and Array Variables
  5. Aside from file handles, so far the only type of variables we've used are scalar variables. Each scalar variable holds a single value (e.g. 5, or "hello"). Perl also enables you to define ordered collections of values, known as lists. Lists are stored in variables called array variables (arrays for short).

    1. Lists

      1. A list is a sequence of scalar values separated by commas and enclosed in parentheses. An example:
        (1, 5.3, "hello", 2)

      2. You can include a scalar variable within a list:
        (1, $number, "hello", 2)

      3. Or even an expression (the expression will be evaluated, and the result will be stored in the list):
        (1, $numberA + $numberB, "hello", 2)

      4. You can also rely on variable interpolation in double-quoted strings:
        ($value, "The answer is $value")

    2. Array variables

      1. Lists are stored in array variables, which are denoted by a leading @ (as opposed to the leading $ used to denote scalar variables). The same naming rules used for scalar variables apply to array variables: only letters, numbers, and underscores are allowed, and the first character following the @ cannot be a number.
        @numbers = (1, 2, 3);

      2. To access an element of an array, you refer to the elements subscript, which indicates it's position in the array. Counting starts at zero, so the second element of an array occupies subscript one.
        @letters = ("a", "b", "c");
        print $letters[1];

        Note that you use a $ instead of an @ when referring to a list element. If this seems confusing, just remember the following rule: whenever you want to refer to a single value, use a $ when referring to it, even if it's in an array.

      3. You can also use a subscript to assign a value to an element of an array.
        @letters = ("a", "b", "c");
        print "the second element of \@letters is $letters[1]\n";
        $letters[1] = "chicken";
        print "the second element of \@letters is now $letters[1]\n";

      4. You can use the list range operator to quickly define a range of list values. This:
        @numbers = (1,2,3,4,5,6,7,8,9,10);

        Is the same as this:

        @numbers = (1..10);

        You can do the same thing with letters (a..z)

        Rules of the list range operator:

        • It increments by one from the value on the left-side of the operator to the value on the right side of the operator
        • It can be used with floating point numbers. @numbers = (2.1..5.3) yields the list (2.1, 3.1, 4.1, 5.1, 5.3)
        • If the value on the left of the operator is greater than the value on the right, the result is an empty list
        • If the values on each side of the operator are the same, the result is that value. @numbers = (3..3) yields the list (3)

      5. You can copy one array to another
        @copy = @original;
      6. You can include one list within another list
        @numbers = (1,2,3);
        $name = "mike";
        @stuff = ("hello", @numbers, $name);

        This yields the following list assigned to @stuff: ("hello", 1, 2, 3, "mike")

      7. You can retrieve the length of an array (i.e. the number of elements in the array) like this:
        @numbers = (1,2,3);
        $howMany = @numbers;
        print $howMany;

        $howMany equals 3.

      8. You can find out the position of the last element in an array like this:
        @numbers = (1,2,3);
        $howMany = $#numbers;
        print $howMany;

      9. You can assign scalar variables from array variables:
        @numbers = (5, 7);
        ($var1, $var2) = @numbers;
        print "\$var1 is $var1\n; # prints 5
        print "\$var2 is $var2\n; # prints 7

      10. You can use print on an array as a way of easily displaying all of the array elements:
        @greeting = ("my", "name", "is", "Mike");
        print "@greeting"; # using quotes puts a space between each element
        print @greeting; # without quotes, there are no spaces between the elements

        When creating an array of strings, you can save yourself some typing by using the qw (quote word) operator:

        @greeting = qw(my name is Mike);
        print "@greeting";

  6. Some Array Functions
    1. sort alphabetically sorts the elements of an array
      @words = ("this", "is", "a", "test");
      print "@words\n";
      @sortedWords = sort(@words);
      print "@sortedWords\n";

      Note that this does not alter the original list: @words is the same as it was before - only @sortedWords contains the alphabetically sorted list.

      Also note that it always does an alphabetic sort. This means that numbers will be sorted alphabetically:

      @numbers = (70,100,8);
      @sortedNumbers = sort(@numbers);
      print "\@sortedNumbers is: @sortedNumbers\n";

      If you're trying to get your numbers sorted numerically, there are two ways to do it. One way is to use a fixed number of digits in each value so that they come out the way you want:

      @numbers = ("070","100","008");
      @sortedNumbers = sort(@numbers);
      print "\@sortedNumbers is: @sortedNumbers\n";

      If you use leading zeros on your numbers for this purpose, be sure to include quotes around them - otherwise Perl will treat them as octal numbers instead of strings.

      The other, and usually more preferable, method allows for an actual numeric sort. If you want to use a sorting method other than the default alphabetic sort, the sort function allows you to use a statement block to specify the desired sorting parameters:

      @numbers = (70,100,8);
      @sortedNumbers = sort { $a <=> $b } @numbers;
      print "\@sortedNumbers is: @sortedNumbers\n";

      The code above uses the numeric comparison operator (also known as the spaceship operator), to numerically compare the list elements to each other. The sort function uses the return value of each comparison (1, 0, or -1) to figure out what order the numbers go in.

    2. reverse flips the order of the elements in an array.
      @backwards = ("backwards", "is", "array", "this");
      @forwards = reverse(@backwards);
      print "@forwards\n";

      reverse is commonly used in combination with sort to provide a reverse alphabetic sorting.

      @months = ("12","02","05","03","11");
      @sortedMonths = reverse(sort(@months));
      print "@sortedMonths\n";

    3. split is actually used on strings, not arrays. The result of a split is assigned to an array variable. For example:
      $line = "This:is:a:string";
      @words = split (/:/, $line, 3);
      print "@words\n";

      The first argument to split is a pattern to search for (in this case, the colon character), enclosed by two slashes. The second argument is the name of the variable to search in. The third argument is optional, and it indicates the number of times to search for the pattern (the search is conducted left to right). If the third argument is omitted, split will find every occurrence of the pattern. The result of the split function is a list, which you can assign to an array variable.

      You'll use split a lot. When you store data in a file, you'll use a character, such as a \t (a tab), or set of characters, to delimit fields of data on a line. For example:

      Toppa\tMichael\t6\t0\n
      Smith\tJoe\t5\t10\n

      When you want to read these fields into an array, you'll use split to separate them from each other.

    4. join is the opposite of split: it takes an array and puts its elements in a single string, with each element separated by a character or set of characters you specify.
      @words = ("This", "is", "an", "array");
      $line = join ("::", @words);
      print "$line\n";

  7. Using Arrays with the localtime Function
  8. Perl has a special function called localtime - when it's called, it returns the current time. When used in a scalar context, it returns a string. In the example below, it's used as an argument to another function, scalar, which forces localtime to be evaluated in a scalar context:
    print scalar(localtime);

    If you call localtime without scalar, it is automatically evaluated as an array. Here's an example:

    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
    $thisYear = $year + 1900; # In Perl, 1900 is year 0, so add 1900 to get the current year
    $thisMonth = $mon + 1; # $mon values are 0 to 11, so add 1 for the real month
    print "Today is $thisMonth/$mday/$thisYear\n";

    The localtime function returns an array of numbers. In the above code we're assigning each of those numbers to a scalar variable. See p. 66 of your book for a breakdown of what each number represents.

    Using arrays, you can print the current day and month:

    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime;
    
    @days = ('Sunday','Monday','Tuesday','Wednesday',
    		'Thursday','Friday','Saturday');
    
    @months = ('January','February','March','April','May','June',
    		'July','August','September','October','November','December');
    
    $realYear = $year + 1900;
    
    print "Today is $days[$wday], $months[$mon] $mday, $realYear\n";

  9. Looping Through Arrays: for and foreach
  10. One way to sequentially access each element of a list is with the while conditional we learned last week:
    @stuff = (1, "chicken", 1.23, "\"Having fun?\"", 9.33e+23);
    $count = 0;
    while ($count <= $#stuff) { 
    	print "element $count is $stuff[$count]\n";
    	$count++;
    }

    Perl provides for and foreach, which allow you to more efficiently access to array elements.

    1. Looping with for

      In the foregoing example of while there are three things you need to control the loop:

      1. A statement that sets the initial value of the loop counter variable
      2. A conditional expression that checks the value of the counter varibale to see whether to continue iterating the loop
      3. A statement that iterates the value of the counter variable

      With a for loop, you can put all of the loop control elements in a single line:

      @stuff = (1, "chicken", 1.23, "\"Having fun?\"", 9.33e+23);
      for ($count = 0; $count <= $#stuff; $count++) {
      	print "element $count is $stuff[$count]\n";
      }

      If you've used other programming languages, this is similar to a for...next loop. In Perl, the next syntax is not used to close the statement block.

      When doing a loop that depends on iteration, it's generally advisable to use a for loop instead of a while loop. When using while, it's easy to forget to iterate the loop.

    2. Looping with foreach

      The most straightforward way to loop through an array is with the foreach statement:

      @words = ("Here", "is", "a", "list");
      foreach $word (@words) {
      	print "$word\n";
      }

      Note the variable $word. It's a "placeholder" that holds the value of the current array element as foreach loops through the array. I did not have to use the variable name $word - I could have used anything I wanted: $item, $x, etc.

      In the above code, $word is a local variable, which means its value is held only for the duration of the loop. If $word had a value before the loop began, that value is restored when the loop ends. For example:

      $word = "Howdy";
      print "\$word is initially: $word\n";
      
      @words = qw(Here is a list);
      foreach $word (@words) {
      	print "\$word is now: $word\n";
      }
      
      print "\$word is again: $word\n";

      To avoid confusion, it's generally not a good idea to have a global variable ($word as it exists outside the foreach loop) use the same name as a local variable. It's too easy to get them confused, and if you did use the same variable name for both, you wouldn't be able to refer to your global variable within your foreach loop, as it's value is temporarily replaced by the local variable.

      We'll talk more about local variables when we get to the topic of subroutines in a later class.

    3. So what are all these loops good for?

      • With many common CGI applications, you'll use foreach loops all the time. For example, you may only want to allow certain web servers to use your CGI application. You can store their names in an array, and then use a foreach loop to check the name of a calling web server against the names in your list. Even more commonly, you will read lines of a data file into an array, and use the foreach loop to make changes to them, or to search for a record.

      • The next most common loops you'll use are while and until. Common uses are reading lines from a file, and checking to see if a file is locked (we'll get to these when we learn how to interact with files). In all of these examples you're not "counting" to maintain your loop - you are simply testing a condition: "Are we at the end of the file yet?", "Is the file still locked?"

      • for loops are used when you need to repeat an operation a specified number of times. For example, if you only need to access the first 10 elements of an array, a for loop is usually the most appropriate choice.

  11. Controlling Loops
  12. Perl offers a variety of methods to control while, for, and foreach loops. Tonight we'll discuss a few of the most commonly used ones:

    1. last exits a loop when an intervening condition is met. For example:
      print "what is your name? ";
      chomp ($input = <STDIN>);
      $count = 0;
      @names = qw(Mike Joe Bob Fred);
      while ($count <= $#names) {
      	if ($names[$count] eq $input) {
      		print "I found your name on my list: $names[$count]\n";
      		last;	
      	}
      	
      	else {
      		print "Your name is not $names[$count]\n";
      	}
      	
      	$count++;
      }

    2. next causes the loop to ignore the rest of the statements in the current iteration, and to skip to the start of the next iteration. In this example, the numbers 0, 1, 2, 4 and 5 are printed. 3 is skipped.
      for ($count = 0; $count <= 5; $count++;) {
      	next if ($count == 3);
      	print "count is $count\n";
      }

    3. Labeled Blocks are used to "name" a loop. In this example, the last statement in the inner while loop is applied to the outer while loop. This effect is achieved by giving the outer loop a label (I called it DONE - you can use any name you wish), and then referring to that label in the last statement.
      $count1 = 0;
      DONE: while ($count1 < 10) {
      	$count2 = 0;
      	while ($count2 < 10) {
      		$count2++;
      		last DONE if ($count1 == 5 && $count2 == 7);
      	}
      	$count1++;
      }
      
      print "$count1" . "$count2";

  13. Next Week's Quiz
  14. The quiz will be one hour long. This is the schedule for next week's class:
    1. 6:15 - 7:20 Quiz (we'll use the first 5 minutes to get everyone situated)
    2. 7:30 - 9:00 Lecture
    3. 9:10 = 10:15 Lab

    The quiz will be open note and open book, but you will not have time to do a lot reading. This means that it's most important to understand the concepts we have covered so far in class, and that it's less important to memorize all the details of syntax. This does not mean your syntax can be wrong! It simply means that it's easier to check up on a detail in your notes during a quiz than it is, for example, to try figuring out a basic Perl concept. For example, before coming in to the quiz you should already have a solid understanding of how a for loop works and what it's used for - you won't have time to read up it during the quiz. But you can bookmark the page that provides the syntax and refer to it if you need to.

    While the quiz is open book, it is not open neighbor. Conversation is not allowed during the quiz - this includes online chats!

    The quiz will consist of four questions. Each one will ask you to write a short script, or make corrections to a script that has errors in it.

    I have to rescind a statement I made in the first lecture. I said that one of the questions would come straight from your lab assignments. I've decided not to do that. Since the test is open book and open note, and the answers to the lab assignments are in my online lecture notes, that would be too easy!

    If you have a laptop computer you're willing to bring to class, please to do so. That way we'll hopefully have enough computers for everyone to take the quiz in B-2. If we don't have enough computers, then some of you can take the quiz in the open lab next door.

  15. Lab: Exercises
    1. Write a script that:
      • Defines an array variable called @me which contains four elements: your first name, your last name, your age, and the name of the town you are from.
      • Uses a for loop which prints each element of the array.

    2. Write a script that:
      • Assigns the value 0 to a variable called $sumAll
      • Assigns the value 0 to a variable called $sumEven
      • Uses a variable called $count to iterate a for loop 10 times.
      • In the for loop, adds the current value of $count to $sumAll
      • In the for loop, adds the current value of $count to $sumEven if $count is an even number.
      • After the for loop is completed, prints the values of $sumAll and $sumEven

    3. Write a script that uses the sort and reverse functions to put the elements of the array @me (from question 1) in reverse alphabetical order. Then use a foreach loop to print each element.

    4. A common task in working with arrays is looking for duplicate values. For example, you may want to know which pages on your web site have been visited. A web server will write the name of a page to a log file every time that page is visited. You can then read the web server log file into an array, and use that array as a basis for creating a report of activity on your web site. To introduce you to this kind of work, write a script that:
      • Has the following statements as the first two lines of code:
        @pages = qw(index.html chapter1.html chapter4.html chapter8.html chapter1.html index.html chapter4.html chapter3.html);
        $previous = "";
      • Defines a new array called @pagesSorted that is an alphabetically sorted copy of @pages
      • Uses a foreach loop to see if there are any duplicate values in the array. To do this, assign the value of the current array element to the variable $previous as the last statement inside the loop. Then, as the first statement inside the loop, you can compare the current array element to the value of $previous.
      • Prints out each element of the array, excluding the duplicates. That is, if an element of the array has the same value as a previous element, don't print it (I leave it up to you to figure this part out!)

    5. Using the localtime function, write a script that indicates how many days there's been between December 25, 1999 and today. See p. 66 of your book, and Section V above, on how to use localtime. Helpful Hints: Last year was not a leap year, so there were 365 days, and December 25 was the 359th day. Also remember that the first day of the year according to Perl is day 0 (so to Perl, the last day of the year was day 364, and December 25 was day 358).

Return to CIS 24 home page