Perl

04. Control Structures


Blocks

Perl contructs "blocks" of code to be executed as a group with curly braces, like C or Java. Curly braces are required with the control structures.

The semi-colon on the last statement in a block is optional, but recommended so you can add more later.


True & False

What is true/false in Perl boolean expression? Mostly like C's "everything but zero is false".

Rules (from Prog Perl 3rd Ed):
if (1) { }           # true
if ("1") { }         # true
if ("00") { }        # true
if ("0.00") { }      # true

if ("") { }          # false
if ("0") { }         # false
if (0.00) { }        # false
if ("0.00" + 0) { }  # false (string converted to number to add)
if (2 - 2) { }       # false (2-2=0)
if (undef) { }       # false (behaves as "")

$x = 7;
if ($x) { }          # true

$x = undef;
if ($x) { }          # false

if/unless

if (expr) {     if (expr) {       if (expr) {
}               }                 }
                else {            elsif (expr) {
                }                 }
                                  else {
                                  }

unless (expr) {                   unless (expr) {
   # do if expr is false          }
}                                 else {
                                  }

Also have condition?then:else shortcut from C.


case/switch

Perl doesn't have one. Sort of. There is a "Switch" module on CPAN, and Perl 6 will be getting one very much like it.

There are many ways to fake it.

example, man perlsyn:

    SWITCH: {
               if (/^abc/) { $abc = 1; last SWITCH; }
               if (/^def/) { $def = 1; last SWITCH; }
               if (/^xyz/) { $xyz = 1; last SWITCH; }
               $nothing = 1;
           }              

while/until

while (expr) {                         until (expr) {
   # do while expr is true                # do until expr is true
}                                      }

do {                                   do {
   # do at least once                     # do at least once
} while (expr);                        } until (expr);
               # note the semi-colon after!

for

Like C's for loop

for (init; expr; re-init) {
   # code
}

for ($i=0; $i < 10; $i++) {
   print "$i\n";
}

foreach

Similar to shell's "for".

foreach $value (@a) {                 foreach $value (reverse(@a)) {
   print "$value\n");                    # blah
}                                     }

foreach $value (keys %hash) {         foreach @a {   # put in $_
   print $hash{$value}."\n";             print;      # prints $_ by default
}                                     }

foreach (@ARGV) {
   print "argument: $_\n";
}

Does not make a copy of the value, refers to real one

foreach $value (@a) {
   $value++;            # actually changes @a !!
}

foreach $value (sort @a) {
   $value++;            # still changes @a !!
}

foreach $value (1, 2, 3) {
   $value++;            # error, modifying read-only value
}

last/next/redo

last causes flow to break out of loop, continue after loop.

next causes flow to return to top of loop, redo the condition.

redo causes flow to return to start of loop but skip checking the condition.

while ($x < 7) {
   if ($x == 5) {
      next;          # don't do work for number 5
   }
   if ($x == 6) {
      last;          # abort if we ever see 6
   }
   # do work
}

These don't work on the "do {} while" and "do {} until" variants.


Labeled Blocks

Use labels on blocks to tell next/last/redo which loop you are referring to on nested loops. Prevents you from having to count loops like in Bourne Shell's break/continue.

OUTER: while ($x < 7) {
       INNER: for ($y=0; $y < 8; $y++) {
                 if ($y == 3) {
                    next INNER;
                 }
                 elsif (($y == 4) && ($x == 3)) {
                    last OUTER;
                 }
              }
       }

Expression Modifiers

You can say "do this, if condition" or "do this, while condition" if that is the way you happen to be thinking.

while (<>) {
   next if ($_ == 7);
}

# same things
until ($i > 128) {              $i *= 2 until ($i > 128);
   $i *= 2;
}

Put the important stuff on the left.


And/Or/Not or &&/||/!

Can use && and || operators between statements like in shell.

Same things:

if (this) {                that if (this);             this && that;
   that;
}

Put the important stuff on the left. We'll see the || a lot with file opening, common to say "open file or die".

Perl has "and" as well as "&&" for logical and-ing two values. Also supports both "or" as well as "||". And does "not" and "!".

The difference between the symbols and the English-looking versions is precedence (English = lowest possible precedence), as well as readability.


Range Operator

.. used to get a range of values.

@letters = ('a' .. 'z');      # 26 values in array

@days = ('01' .. '31');       # 31 leading-zero values (strings incremented)

for $x (1 .. 100) {
   # do something 100 times
}

Worse: can be used to filter lines of data by line number or regex.

while (<>) {
   if (2 .. 5) {          # true on 2nd, 3rd, 4th and 5th lines only
      print;              #  implicit test of $.
   }
}

while (<>) {
   next if (1 .. /^START/);     # skip lines from first through one marked START
   print;
}