5. Control Flow

Created Friday 18 April 2014

USING THE IF STATEMENT

if/else/elsif/unless

if ( ... ) {
} elsif ( ... ) {
...
} else {
}

Examples
if (@names) {
# won't execute if @names is empty
}
if (%names) {
# won't execute if %names is empty
# In scalar context, a hash returns a string with the number of 'buckets' used, followed by a forward slash, and then followed by by the number of 'buckets' allocated for that hash. This is sometimes useful for debugging hash problems but otherwise has little practical use. However, if the hash is empty, it returns 0 (zero) in scalar context, allowing the if (%names) { ... } construct to work
}
if ( my $customer = get_customer($id) ) {
# only executed if $customer evaluates to true
}

The Ternary Operator ?:

VALUE = CONDITION ? IFTRUE : IFFALSE
Example:
my $max = ( $num1 < $num2 ) ? $num2 : $num1;
Chaining ternary operators:
my $max = ( $num1 < $num3 and $num2 < $num3 ) ? $num3
: ( $num1 < $num2 ) ? $num2
: $num1;

FOR/FOREACH LOOPS

Arrays

for my $number (@numbers) {
print "$number\n";
}
foreach my $number (@numbers) {
print "$number\n";
}
More
my @numbers = ( 5, 6, 7 );
foreach (@numbers) {
print "$_\n";
}
To remove newlines from each element and if the element evalueates as true — to print it:
for (@names) {
chomp;
if ($_) {
print;
}
}
Taking advantage of aliasing ($_ or a named var is an alias to the element in question, so it's possible to alter an array via this var):
my @numbers = ( -7, -5, -1, 0, 3, 6, 29 );
for my $number (@numbers) {
if ( $number < 0 ) {
$number = 0;
}
}
print join ',', @numbers;

Lists

# In this case the hash won't be modified because keys() (like sort()) returns a new list
my %economic_description = (

libertarians => 'Anarchists with jobs',
anarchists => 'Libertarians without jobs',
randroids => 'Closet libertarians',
democrats => 'the tax and spend party',
republicans => 'the tax cut and spend party',
);
for (sort keys %economic_description) {
my $description = lc $economic_description{$_};
$_ = ucfirst;
print "$_ are $description.\n";
}

# Range operators, when used in list context, also return a list
# In this case the values of the returned list aren't assigned to anything, so they are
# anonymous variables, and this means you can change them
for my $number ( -10 .. 10 ) {

$number++;
print $number;
}

C-Style

for (EXPRESSION ; EXPRESSION ; EXPRESSION) BLOCK

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

print "$i\n";
}
OR
my $i = 0;
for (; $i < 10; ) {
$i++;
print "$i\n";
}
If you need the index of an array:
for (my $i = 0; $i < @array; $i++) {
print "$i: $array[$i]\n";
}
Or, the cleaner version:
for my $i (0 .. $#array) {
print "$i: $array[$i]\n";
}

Example where the C-style for loop is handy:
for (my $i = 0; $i <= 25; $i += .25) {

my $amplitude = int(40 + 35 * sin($i));
print " " x $amplitude;
print ".\n";
}
Whithout the C-style it would be like this:
for my $i (0 .. 100) {
$i = $i / 4;
my $amplitude = int(40 + 35 * sin($i));
print " " x $amplitude;
print ".\n";
}
The variable increment may be set within the program
for (my $i = 7; $i < 10; $i += $user_choice) {
print "$i\n";
}

WHILE/UNTIL LOOPS

while ( TRUE_EXPRESSION ) BLOCK
until ( FALSE_EXPRESSION ) BLOCK

my $i = 10;
while ( $i > 0 ) {

if ( rand(3) > 2 ) {
$i++;
} else {
$i--;
}
print $i,$/;
}

http://www.perlhowto.com/iterate_through_a_hash
while ( ($key, $value) = each %hash )
{
print "key: $key, value: $hash{$key}\n";
}

last/next/redo/continue

Using last()

Find the first perfect square in an array:
my @numbers = ( 3, 7, 9, 99, 25 );
my $first;
for my $number (@numbers) {
my $root = sqrt($number);
if ( int($root) == $root ) {
$first = $number;
last;
}
}
if ( defined $first ) {
print "The first perfect square in the array is $first\n";
} else {
print "No perfect square found in array\n";
}

Using next()

Find all perfect squares:
my @numbers = ( 3, 7, 9, 99, 25 );
my @perfect_squares;
for my $number (@numbers) {
my $root = sqrt($number);
if ( int($root) != $root) {
next; # skip the rest of the loop BLOCK
}
print "Found perfect square: $number\n";
push @perfect_squares, $number;
}

Using the continue Statement

for (EXPRESSION) BLOCK continue BLOCK
while (EXPRESSION) BLOCK continue BLOCK

Perfect roots again:
use strict;
use warnings;
my @numbers = ( 3, 7 ,9 ,99, 25 );
my @perfect_squares;
for my $number (@numbers) {
my $root = sqrt($number);
if ( int($root) != $root) {
next; # skip the rest of the loop BLOCK
}
print "Found perfect square: $number\n";
push @perfect_squares, $number;
} continue {
print "Processed $number\n";
}

Using the redo Statement

Labels

Example
NUMBER: foreach my $number (@numbers) {
# lots of code
if ($some_condition) {
next NUMBER;
}
# more code
}
Find any strings in the first array that are substrings of any strings in the second array
my @strings1 = qw( aa bb cc dd ee );
my @strings2 = qw (
an
intelligent
robber
needs
a
good
ladder
);
my @found;
DOUBLED_LETTER: foreach my $double (@strings1) {
foreach my $word (@strings2) {
if ( index($word, $double) != -1 ) {
push @found, $double;
next DOUBLED_LETTER;
}
}
}
print "@found";

STATEMENT MODIFIERS

STATEMENT if EXPRESSION;
STATEMENT unless ESPRESSION;
STATEMENT while EXPRESSION;
STATEMENT until EXPRESSION;
STATEMENT for LIST;
STATEMENT foreach LIST;

Examples
print "We can has cheez" if $trite;
print "We have a valid user: $user\n" if $user;
my @array = ( 1 .. 5 );
print "$_\n" foreach @array;
my $countdown = 10;
print "$countdown\n" while $countdown--;
go_outside() and play() unless $is_raining; # If go_outside() returns false then play() won't be executed, so
go_outside(), play() unless $is_raining;

do while/do until

do BLOCK
do BLOCK while EXPRESSION
do BLOCK until EXPRESSION;

Example
my $factorial = 1;
my $counter = 1;
do {
$factorial *= $counter++;
} while $counter <= 5;
print $factorial;

GIVEN/WHEN

given (EXPRESSION) BLOCK

when (EXPRESSION) BLOCK

Example
my $number = 1;
given ($number) {

when(0) { print "The number is 0"; }
when(1) { print "The number is 1"; }
when(2) { print "The number is 2"; }
default { print "The number is unexpected"; }
}
Example (using $_)
given ($number) {
when ($_ < 0) {
print "The number is negative";
}
when ($_ > 0) {
print "The number is positive";
}
default {
print "The number is 0";
}
}
To test subsequent when statements, you can use the continue keyword
given ($word) {
when ( lc $_ eq scalar reverse $_ ) {
print "'$word' is a palindrome\n";
continue;
}
when ( length($_) > 10 ) {
print "The length of '$word' is greater than 10 characters\n";
}
}
for ($number) {
when ($_ < 0) {
print "The number is negative";
}
when ($_ > 0) {
print "The number is positive";
}
default {
print "The number is 0";
}
}



Backlinks: