Thursday, 3 April 2014

Loops and conditionals: Ifelsifelse

The next things on my list are conditionals and loops. I think that these concepts are fundamental to actually making any programming language work and without them, there wouldn't be much to do, especially when combined with conditional logic.

The first one is the if statement. It's basic structure is something like this:

if (condition) {
    code block
}

This evaluates the condition in the parentheses, and if it's true, the code block will be run and if it's false, the code block will be passed over and the rest of the file will be run.

Boolean Logic

How do I make the condition equal to true or false?
Perl doesn't actually have any specific true or false objects or identifiers so we have to make the condition evaluate to values that themselves are either true or false.

What evaluates to true or false?
Basically everything is true, apart from undef, "0", an empty string ("") and anything that evaluates to any of these.

The table below shows how you can get true or false values:

True False
"0.0" ""
" " "0"
1 undef
+ve integers 0 # converts to "0"
-ve integers 0.0 #computes to 0 and then converts to "0"
strings unassigned variable  #evaluates to undef

()  #empty list

("") #empty string in a list

You can put any of these as the condition, for example:

1.   if (7) {
2.     code block
3. }

1.   my $value = 0.0;
2. if ($value) {
3.     code block
4. }

The code block will run in the first example because 7 is a positive integer and positive integers evaluate to true. The code block will not run in the second example because 0.0 evaluates to false.

You don't have to put single values into the condition - you can also use boolean operators to make things more interesting and with these, you can make expressions that evaluate to true or false.


Boolean Operators Meaning
> Greater than (numerical)
< Less than (numerical)
== Equal to (numerical)
gt Greater than (string)
lt Less than (string)
eq Equal to (string)
! Not
&& And
|| Or

1.   my $temperature = 28;
2. if ($temperature >  25) {
3.     print "it's hot!";
4. }
1.   my $value = 4;
2. if ($value <  8  &&  $value > 5) {
3.     print "this number is between 5 and 8";
4. }
1.   my $string = "hello";
2. if ( ($string eq "hello") || ($string eq "hi") {
3.     print "hello there";
4. }

Of course you can put more interesting things inside the code blocks than just a print statement, but you get the idea.

You can also experiment with making things more complicated and incorporate as many ands and ors as you want and and combination of boolean operators or do crazy things like XOR and NAND.

To use not, you just put the exclamation mark in front of the expression you want to evaluate:
(!($string eq "hello"))
I think the inside brackets are optional but it makes things clearer and shows that you want to negate the whole of the expression rather than just the variable.

Note
If you try to compare two numbers using the string equal to operator (eq) the numbers will be stringified and then compared. If your two numbers are the same, the condition will evaluate to true as the strings will be the same. If the numbers are different, you will get false.

If you try to compare two strings using the numerical equal to operator (==) you will get warnings but it will always evaluate to true, even if the strings are not the same. This is because strings are evaluated as 1 and then you will be comparing two 1's.

Else

Sometimes you want something to happen if the if condition isn't satisfied. You can then add extra code to say what you want to do if the if condition evaluates to false by using else. The else immediately follows the if as follows:

if (condition) {
    code block
}
else {
    code block
}

This way some code will always be run no matter what the condition evaluates to. If it evaluates to true, only the first code block is run, if it evaluates to false, only the second code block is run. Then the rest of the file is then run as normal.

This example could work in a shop selling alcohol (can you tell I used to work in a supermarket?):

1.   if ($customer_age > 17 {
2.     print "sale authorised";
3. }
4. else {
5.     print "sale denied";
6. }

Elsif

What happens if you want to put multiple ifs together? Say that you want to test one condition, and if it's not satisfied, you want to test another condition. This is where elsif comes in, and for reasons unknown to me, it is spelt with a missing 'e'. Excellent.

if (condition) {
    code block
}
elsif (condition) {
    code block
}

If neither of the conditions are satisfied, neither of the code blocks will be run. If the first and the second condition are satisfied, only the first code block will be run because the second conditions is only evaluated if the first condition is not true.

For example:
1.   my $x = 50;
2. my @small_numbers;
3. my @medium_numbers;
4.
5. if ($x < 101) {
6.     push(@small_numbers, $x)
7. } elsif ($x < 201) {
8.     push(@medium_numbers, $x)
9. }

This code is just looking at the number and putting it into an array of small or medium numbers. So here I've defined small as 100 or less and medium as 200 or less. If the number is larger than 200, nothing will happen. I know this makes no sense in the real world but I think it works as an example.

You can write it like this or you can add an "else" onto the end if neither of the two conditions are satisfied.

You can chain as many elsifs as you want together but only on else can be put onto the end.

I couldn't think of a real life example so, again, here are a bunch of numbers:

1.   my $x = 5;
2. my @small_numbers; 
3. my @medium_numbers;
4. my @large_numbers;
5. my @very_large_numbers;
6.
7. if ($x < 4) {
8.     push (@small_numbers, $x);
9.  } elsif ($x < 8) {
10.     push (@medium_numbers, $x);
11.  } elsif ($x < 20) {
12.     push (@medium_numbers, $x);
13.  } else {
14.     push (@very_large_numbers, $x);
15.  }

This code is just looking at the number and putting it into an array of small, medium, large or very large numbers. So here I've defined small as below 4, medium between 4 and 7, large between 8 and 19 and very large as 20 and above. I know this makes no sense in the real world but I think it works as an example.

Again, make sure you spell elsif properly!!!


Unless

This one is the exact opposite of if and when I see it, it always messes with my head and I have to think about it for a moment - every single time! I think that it's used when it looks cleaner to use rather than having double negatives everywhere or having lots of ands and ors.

unless (condition) {
    code block
}

Here the code block only runs if the condition is not satisfied:
1.   unless ($age < 18)
2. {
3.     print "Sale approved";
4. }
This just means that sale approved will only be printed if the customer is 18 or over.

4 comments:

  1. "Unless" certainly has its place. You do see it abused occasionally, with things like "unless (!$true)" or, worse, the unless-else :)

    There's also the trailing version of if/unless:
    print "Sale approved" unless $age < 18

    ReplyDelete
  2. I like else a lot. But it's banned from many codebases (I think Damian bans it in PBP, so it's one of the default Perl::Critic rules).

    If course, if you're golfing, "if (!$flag)" is shorter than "unless ($flag)". But I think the unless version is more readable.

    ReplyDelete
  3. Keep up the good blogs. I've been learning Perl since the beginning of the year and am right around the same pace as yourself and seem to also be learning in the same order. I've been watching the video set from the below youtube channel along with mimicking what he does in Notepad++ which has been very helpful. Here the series I've been watching: https://www.youtube.com/watch?v=APF18i3TU40

    ReplyDelete
    Replies
    1. Thanks for the tip! Good luck learning Perl

      Delete