Flow Control and Subroutines

Blocks

Blocks in Perl are denoted by { and }. Blocks retain their lexical scope:

 use strict;
 {
    my $amount = 123.45;
 }
 print($amount);  # BOOM

Simple flow control

If statements have the keywords: if / elsif / else. Elsif and else are of course optional. Every action after the if, elsif or else must be a block: { and } are required, even if the block contains only one amount!

 if ($amount < 12) {
    print("Amount is less than twelve\n");
 } elsif ($amount < 20) {
    print("Amount is less than twenty\n");
 } else {
    print("Amount is twenty or more\n");
 }

Unless is the reverse of if (it's an if-not):

 unless ($amount >= 0) {
    print("You have overdrawn your account\n");
 }

If and unless can be reversed for more compact syntax:

 print("Bring your umbrella\n") if ($rain_is_falling);
 print("You have overdrawn your account\n") unless ($amount >= 0);

Iteration

Perl has the usual iteration constructs: while, do/while, for:

 my $i;

 $i = 0
 while ($i < 10) {
    print("i is ", $i);
    $i++
 }

 for ($i = 0; $i < 10; $i++) {
    print("i is ", $i);
 }

If you only want to use the control variable (above: $i) inside the for-loop then you can define it using 'my' in the for-statement:

 for (my $i = 0; $i < 10; $i++) { .... }

To iterate over an array, the "for my .." construct can be used:

 my @arr = ('a', 'b', 'c', 'd');
 for my $el (@arr) {
    print($el, "\n");  # prints a, then b, etc.
 }

Next and last

Next and last are like 'break' and 'continue' in other languages: next forces the loop to continue; last forces the loop to stop. For example the following loop only prints 0 to 5:

 for (my $i = 0; $i < 1000; $i++) {
    print("i is now ", $i, "\n");
    last if (i == 5);
 }

Exercise

Use the following code fragment:

 for (my $i = 0; $i < 1000; $i++) {
    # insert your statement(s) here
    print("i is now ", $i, "\n");
 }

The loop must print:

 i is now 1
 i is now 2
 i is now 3
 i is now 8

(and nothing more).

Subs

Functions or procedures are in Perl known as subroutines, abbreviated 'sub':

 sub mysubroutine {
    # block with statement(s)
 }

The subroutine definition doesn't state whether there are arguments, and if so, how many. It always gets the magic array called _ (underscore), so that is typed as @_, which can be inspected. The first argument, if any, is $_[0], the second one is $_[1] and so on.

The caller (invoker) simply puts the arguments in a list, so using ( and ), and the arguments are separated with a comma. Just like the print-examples used before.

Exercise

Write a non-recursive (iterative) version of the factorial function. For example the following code must print 120:

  print("Factorial 5 is ", fac(5), "\n");

Next write a recursive version of the function.

Exercise

Write a Perl program that lists the contents of a directory, and recurses into subdirectories and lists those. The program can be invoked without an argument, in which case it generates a list starting at the current directory, or with an argument, in which case it starts listing there. E.g., if the script is called 'rec.pl', then:

  perl rec.pl /tmp       # lists /tmp and subdirs
  perl rec.pl            # lists current dir and underneat

The building blocks you will need are: