Logicals: If all else fails...

Overview:

  • Teaching: 10 mins
  • Exercises: 10 mins

Objectives:

  • Introduce logical operations &, &&, |, || and ~
  • Introduceif-else and if-elseif-else statements

It's logical, you'll see

As you saw in the previous lesson, while-loops check a given condition each time the commands in the loop are executed. It is natural to simply "read" the syntax for while-loops as you see them, for example reading

while x<10

literally as "while x is less than 10". However this isn't quite how MATLAB sees it.

Consider what happens if we execute the following:

In [1]:
x = 0;
disp(x<10)
1

MATLAB will print the value of x<10 as being 1. What if we set x so that the statement doesn't hold?

In [2]:
x = 11;
disp(x<10)
0

In this case, the "value" of x<10 is 0. In fact, we can save the value of this expression to a variable:

In [3]:
x = 0;
y = 11;
xStatement = x<10;
yStatement = y<10;

You'll see the variables xStatement and yStatement appear in your variable workspace, but look at what type (or class, if using an earlier version of MATLAB) of variables they are. These are not floats or chars (strings) - they are logicals.

logical is a variable type used by MATLAB to interpret the truth value of statements. x<10 is a statement which returns either false (displayed as 0) or true (displayed as 1), this value of true or false is stored as a logical variable. As such the syntax for a while-loop is better read as "while some condition is true".

Since logical is a variable type, we can perform logical manipulations (like "and", "not" and "or") with them that you should be familiar with from your first logic/analysis course:

In [4]:
tf1 = true;
tf2 = false; %you can directly create logicals by using the keywords true and false

%we can use the & symbol for "and"
disp(tf1 & tf2) %tf1 and tf2
disp(tf1 && tf2) %tf1 fast-and tf2

%we can use | for "or"
disp(tf1 | tf2) %tf1 or tf2
disp(tf1 || tf2) %tf1 fast-or tf2

%we can use the tilde ~ for "not"
disp(~tf1)
disp(~tf2)
0
0
1
1
0
1

If we have logical operations, we can put more complex conditions into our loops:

In [5]:
x = 2;

while x>0 && x<6-1e-8
    x = 3 + x/2;
    fprintf('New x value: %.10f \n', x)
end %while, x<0 && x<6-1e-8

%%this sequence converges to 6, so putting x<6 will result in a while loop that never terminates!
New x value: 4.0000000000 
New x value: 5.0000000000 
New x value: 5.5000000000 
New x value: 5.7500000000 
New x value: 5.8750000000 
New x value: 5.9375000000 
New x value: 5.9687500000 
New x value: 5.9843750000 
New x value: 5.9921875000 
New x value: 5.9960937500 
New x value: 5.9980468750 
New x value: 5.9990234375 
New x value: 5.9995117188 
New x value: 5.9997558594 
New x value: 5.9998779297 
New x value: 5.9999389648 
New x value: 5.9999694824 
New x value: 5.9999847412 
New x value: 5.9999923706 
New x value: 5.9999961853 
New x value: 5.9999980927 
New x value: 5.9999990463 
New x value: 5.9999995232 
New x value: 5.9999997616 
New x value: 5.9999998808 
New x value: 5.9999999404 
New x value: 5.9999999702 
New x value: 5.9999999851 
New x value: 5.9999999925 

So long as the value of the statement that you provide comes out to be true, MATLAB will run the while-loop again!

NOTE: This can result in infinite loops - loops that never terminate! The simplist example of these is the code:

x = 0;
while true
    x = x+1;
end %while true

DO NOT execute this in the command line, because MATLAB will diligantly sit there adding 1 to x for all time. You can force MATLAB to stop running the current script, function, or in general code that you've provided to it by hitting CTRL+c when in the command line. Because of this reason, when you use while loops you should only do so if you can guarantee that the process will end eventually; if you can't do this then common practice is to set a manual maximum iterations variable and use that to exit the loop:

maxItt = 1000;
currItt = 1;
while <CONDITION> && currItt<=maxItt
    <STUFF YOU WANT TO DO>

    currItt = currItt+1;
end %while

The condition currItt<=maxItt will force the while-loop to end if it goes on too long.

Fast-and fast-or, how are they fast-er?

As you can see from the above, MATLAB has a fast-and operator (&&) and fast-or (||) operator, which are employed by repeating the and (&) and or (|) operators. So what's the difference between the fast version and the regular (or slow, depending on your outlook) version? The fast operators work by only checking the value of the second logical variable if the result is not entirely determined by the first. For example, the statement "true or false" is always true, and you can determine this simply by looking at the first value (true) and knowing that you're performing an "or" operator - it doesn't matter what the second value is, so why bother checking it? Hence the word "fast": && and || are faster than their couterparts because they don't check the second logical value if they don't need to, whilst & and | do.

So follow-up question - why use & and | at all? The answer is because && and || only work on scalars - they can't be used to compare values element-wise. Yes, you read that right - MATLAB lets you define arrays of logicals and compare those too:

In [6]:
logical_array1 = [true true false true false];
logical_array2 = [false false true true true];
disp(logical_array1 & logical_array2) %element-wise and
disp(logical_array1 | logical_array2) %element-wise or
disp(~logical_array1) %element-wise not
  0  0  0  1  0
  1  1  1  1  1
  0  0  1  0  1

Notice how the output of each command is another logical array.

if statements

The final type of conditional statement that MATLAB has is the if-elseif-else statement. The syntax is as follows:

if <CONDITION>
    <DO SOME COMMANDS>
elseif <CONDITION>
    <DO SOME DIFFERENT COMMANDS>
else
    <DO SOME ALTERNATIVE COMMANDS>
end %if <CONDITION>

Upon reaching an if statement, MATLAB will check the condition immediately after the if keyword. If the condition comes back as true, the commands that follow the if are run, and the commands after the elseif and else are ignored. If the condition comes back as false, MATLAB then checks the elseif condition; if this condition is true then the commands after the elseif are run, and the other sets of commands are ignored. If the elseif condition is also false, MATLAB runs the commands after the else until end. Notice that after else there is no condition to check - this is the case when "all other options have failed", so to speak.

Let's look at a simple example:

In [7]:
myNumber = rand(1); %generate a uniform random number between 0 and 1
fprintf('Random number generated was: %.5f\n', myNumber)

if myNumber>2/3
    fprintf('The random number generated is greater than 2/3 \n')
elseif myNumber>1/3
    fprintf('The random number generated is between 1/3 and 2/3 \n')
    %note: if we have to check the elseif condition, we know that the if condition was false. 
    %Thus myNumber is not greater than 2/3; IE is less than 2/3, should we be running these commands
else
    fprintf('The random number generated is less than 1/3 \n')
end %if, myNumber>2/3
Random number generated was: 0.01298
The random number generated is less than 1/3 

Of course, ranther than simply displaying the range that a value falls into, we can perform computations based on the cases in the if statement. For example, we could track the distribution of the random numbers MATLAB was generating:

In [8]:
nTrials = 1000;
upperThirdCounter = 0;
middleThirdCounter = 0;
lowerThirdCounter = 0;

for i=1:nTrials
    randNumber = rand(1);
    
    if randNumber>2/3
        upperThirdCounter = upperThirdCounter + 1;
    elseif randNumber>1/3
        middleThirdCounter = middleThirdCounter + 1;
    else
        lowerThirdCounter = lowerThirdCounter + 1;
    end %if, randNumber>2/3
    
    %note that we don't use the loop variable i anywhere in this loop.
    %but the loop will still run 1000 times, changing the value of i each time (even though we don't use it)!
end %for, i

fprintf('# Values >2/3: %d \n', upperThirdCounter)
fprintf('# Values between 1/3 and 2/3: %d \n', middleThirdCounter)
fprintf('# Values <1/3: %d \n', lowerThirdCounter)
# Values >2/3: 329 
# Values between 1/3 and 2/3: 330 
# Values <1/3: 341 

What if I don't need elseif or else?

You may find yourself in a situation where you don't want to do anything if your conditions are false, or you have a yes/no situation where an elseif condition doesn't make sense. Here MATLAB (like most programming languages) is quite flexible, in that you don't actually need to include elseif or else in an if statement.

If there are no elseif or else keywords, MATLAB will interpret these as "do nothing". For example, the following code will happily execute without any errors:

thisLessonIsFun = true;

if thisLessonIsFun
    fprintf('Glad you''re enjoying it :) \n')
else
    fprintf('That''s a shame, please give me some feedback. Or just shout at Will - that makes most people feel better.\n')
end %if

as will this code:

englandWonTheFootballWorldCup = false
englandWonTheCricketWorldCup = true

if englandWonTheFootballWorldCup
    fprintf('Football''s coming home! \n')
elseif englandWonTheCricketWorldCup
    fprintf('Cricket''s coming home! \n')
end %if

and even this code:

youreStillReadingThis = true

if youreStillReadingThis
    fprintf('Thank you for your attention \n')
end %if

You can also put in MULTIPLE elseif keywords, each one adds another case for MATLAB to check. These are checked in the order they appear, so be careful:

myNumber = rand(1); %generate a uniform random number between 0 and 1
fprintf('Random number generated was: %.5f\n', myNumber)

if myNumber>3/4
    fprintf('The random number generated is greater than 3/4 \n')
elseif myNumber>2/4
    fprintf('The random number generated is between 3/4 and 2/4 \n')
elseif myNumber>1/4
    fprintf('The random number generated is between 2/4 and 1/4 \n')
else
    fprintf('The random number generated is less than 1/4 \n')
end %if, myNumber>3/4

Grade Classification

Load in the data in the file studentMarks, available here. This file contains the marks of several students who have just sat their final university exams. Each array (creatively named student followed by a number) is a 4-by-10 array of integer scores; each entry being the mark awarded in a given exam. Each row of these arrays corresponds to the year of study in which the exam was taken - scores in the first row are exams taken in the first year, those in the second row the second year, and so forth.

It is your job to write a function that can determine each student's grade given their array of marks. This function should take as an argument one of the arrays of marks, and return a string variable with the classification.

Grades are determined in the following way:

  • The student's overall score is determined by the following formula:

    overall_score = 0.06*first_year_average + 0.10*second_year_average + 0.32*third_year_average + 0.50*forth_year_average

  • If the student attains an overall_score of greater than or equal to 70, they are awarded a first-class.
  • If the student attains an overall_score of greater than or equal to 68, and a forth_year_average of greater than or equal to 70, they are awarded a first-class.
  • If the student attains an overall_score between 60 (inclusive) and 70 (exclusive), and doesn't qualify for a first-class, they are awarded a 2:1-class.
  • If the student attains an overall_score between 50 (inclusive) and 60 (exclusive), they are awarded a 2:2-class.
  • If the student attains an overall_score between 40 (inclusive) and 50 (exclusive), they are awarded a 3rd-class.
  • If the student attains an overall_score between 38 (inclusive) and 40 (exclusive), and has a third-year average of greater than 70, they are allowed to resist their 4th year. This is classed as resit.
  • If the student fails to attain any other classification, they are awarded (?) with a fail.

You should use fprintf to print out the award classification the student gets. To print a string, you need to use the %s format specifier.

Use your function to determine the grades of each of the students' marks that you have available.

Solution

Key Points:

  • if statements execute commands after checking a (possibly several) condition(s).
  • if statements are useful for case-handling in programs.
  • elseif and else keywords do not need to be provided.
  • We can have as many elseif keywords as we like, and they are checked in the order they appear.