6. Conditional

6.1 Blocks

To understand conditionals, you've to understand how to organize statements (and loops - chapter 7) in general. The answer is: by blocks. A block is a section of code between curly braces. An example of a simple block:
{
$a = 1;
$b = 2;
}
Nested blocks (described as parent and child block or outer and inner block) are also common, examples of which you'll meet later (chapter 14).

A block can be preceded by a label: a block becomes a name, which can be used by code.
INIT: {
$a = 1;
$b = 2;
}
How to use a named block will be explained later.

6.2 True or false

Conditionals use the values true and false. True is simply not false. False is

  • number 0 or 0.0
  • an empty string
  • the string "0" (null) # ...but "0.0" is true
  • an empty list ()
  • undef
...and of course the following expressions are false:
  • 9 < 8
  • -5 + 5
  • "0.00" + 0 # evaluated to 0, i.e. false
Notice that a string that contains one or more spaces is evaluated to true!

6.3 The conditional 'if'

With the block concept you can define conditional structures in general easily.
if ( condition ) block_if_true
if ( condition ) block_if_true else block_if_false
The result of the evaluation of ( condition ) is true or false. If the result equals true, the first block will be executed, otherwise -if present- the second block. An extension of this is the following:
if ( condition ) block_if_true elsif ( condition ) block_if_true ... else block_if_false
For now, 'if' has to do with comparing scalars, i.e. numbers or strings (1). Let's start with a tricky one:
if (not defined $test_not_defined) { # or if (! defined $test_not_defined)
print ("true ... \$test_not_defined is not defined");
}
The normal cases:
$test = 1;
if ($test) { # or more precise: if ($test == 1) ...
print ("\$test is $test and true"); # this will be executed...
}
$test = 0;
if ($test != 0) {
print ("\$test is $test and true");
}
else {
print ("\$test is $test and false"); # this will be executed...
}
Or:
$test = 2;
print( ($test == 2) . "\n"); # this will print 1, i.e. TRUE
You can extend the 'if-else' with 'elsif':
Game: guess my number (1, 2 or 3)
$your_guess = 3;
if ($your_guess == 1) {
print ("\$your_guess is $your_guess but not my number...");
}
elsif ($your_guess == 2) {
print ("\$your_guess is $your_guess but not my number...");
}
else {
print ("\$your_guess is $your_guess and also my number!"); # this will be executed...
}

6.4 Extended conditionals with AND (&&) and OR (||)

$a = 1;
$b = 3;
if ($a == 1 AND $b == 2) {
print ("\$a is equal to 1 AND \$b is equal to 2");
}
else {
print ("\$a is equal to 1 AND \$b is unequal to 2"); # this will be executed...
}
$user="Reinier";
if ($user eq "Reinier" OR $user eq "Barbara") {
print ("The name of the user is 'Reinier' or 'Barbara'"); # this will be executed...
}
else {
print ("The name of the user is not 'Reinier' and also not 'Barbara'");
}
The extended conditional makes nested 'if' constructions unnecessary.
6.4.1 Shorter code with conditionals
$name = "reinier";
if ($name eq "reinier") {
    print(uc($name));
}	
This is equivalent with:
$name = "reinier"; 
($name eq "reinier") && ( print(uc($name)) );
I prefer the postfix 'if' (6.5), which is for me more readable.

6.5 Postfix 'if'

Statement modifiers use 'if' in a postfix form, e.g.
$test = 1;
print ("\$test is $test and true") if ($test);
The prefix form of this statement is the first example of 6.6. The same result gives a negated condition:
$test = 0;
print ("\$test is $test and not 0 is true") if (! $test);
With multiple statements
$test = 0;
( $x = 0, $y = 0 ) if (! $test);
or
$test = 0;
do { $x = 0; $y = 0; }  if (! $test);    

6.6 Ternary operator

The conditional ternary operator takes three operands:
(condition) ? expression_if_true : expression_if_false;
If condition is true, then expression_if_true will be executed, otherwise expression_if_false.
$no = int(rand(10));
($no % 2) ? print("the number $no is odd\n") : print("the number $no is even\n");
or shorter:
$no = int(rand(10));
print ("the number $no is ", ($no % 2) ? "odd\n" : "even\n");
Multiple statements are also possible:
$no = 56;
($no > 70 && $no < 100 ) ? ($r = "true0") 
                         : ( ($no > 55 && $no < 60 ) ? ($r = "true1") : ($r = "false1") );
print $r;
The conditional operator results into shorter code on a single line. But -as always- avoid unreadable code, using the ternary operator.

6.7 'unless'?

According to Perl Best Practices, you should never use 'unless' (which is a negated 'if'). I agree. Using 'unless' can lead to completely unreadable code. So don't use 'unless'. All you want, can be done with 'if'!

6.8 'switch'?

Perl does not know the switch-case statement. But you do not need the 'switch-case' statement: all can be done with 'if'. The following code using statement modifiers is for me equivalent to a 'switch-case' statement:
$value = "4";
SWITCH: {
print ("3\n") if ($value eq "3");
print ("4\n") if ($value eq "4");
die ("Number not equal to 3 or 4\n"); # '\n' is necessary to avoid an error message!
}
The comparable code with the Switch module (metacpan.org/pod/Switch) is:
use Switch;
$value = 3;
switch ($value) {
case 1 { print ("1\n") }
case 2 { print ("2\n") }
else { print ("Number not equal to 1 or 2") }
}
There is a difference: the 'die' command ends the program, the 'else' does not. It's easily 'repaired':
$value = 5;
SWITCH: {
print ("3") if ($value == 3);
print ("4") if ($value == 4);
print ("Number not equal to 3 or 4\n") if ($value != 3 and $value != 4);
}
The last code line can be made more flexible with:
  print ("Number not equal to 3 or 4\n") if (grep (/^$value$/, (3,4)));
The regex '/^$value$/' matches both the beginning and the end of the string, i.e., the regex matches the whole $value.
The regex '/$value/' would match any value that contains $value and that is not what you want here. We will discuss regex extensively in chapter 9.

Alternative regex:
  print ("Number not equal to 3 or 4\n") if ($value !~ [3,4]);

Exit: ASCII values in string comparisons

$result = "dog" lt "cat" # $result will be 0: the ASCII value of 'd' is greater than 'c'
$result = "321" gt "62" # $result will be 0: the ASCII value of '3' is lower than '6'


Footnotes
(1) Numerical operators for comparison: ==, !=, <, >, <=, >= The string comparison operators are: eq, ne, lt, gt, le, ge. Their meaning is repectively: equal, not equal, lower, greater, lower or equal, greater or equal