1.5. Conditionals#

A conditional statement allows a block of code to be executed only when a given logical condition is satisfied. Before introducing the so-called if statement, we need to learn how to express these conditions.

1.5.1. Boolean expressions#

A boolean expression is an expression that is either true or false. For example, the operator == compares two operands and returns true if they are equal and false otherwise:

123 == 123
true
123 == 100 + 23
true
123 == -123
false

true and false are special values that belong to the type Bool:

typeof(true)
Bool
typeof(false)
Bool
typeof(45 == 45)
Bool

Other comparison or relational operators are listed below

Operator

Name

==

equality

!=,

inequality (type \ne + TAB)

<

less than

<=,

less than or equal to (type \le + TAB)

>

greater than

>=,

greater than or equal to (type \ge + TAB)

and here are some more simple examples:

23  -5
false
4  44
true

Note that comparison between floating point numbers and integers work as you would expect:

1.0 == 1
true
1.000000000001 == 1
false

Finally a warning about notation: A common mistake is to use single = for comparison (since it mimics the mathematical notation), but this is incorrect. Single = is an assignment operator, double == is a comparison operator.

1.5.2. Logical operators#

More complex boolean expressions can be formulated using the following logical operators:

&& (and), || (or), and ! (not).

Note that && has higher precedence than || does.

Examples:

3 < 4 && -15 > -10
false
3 < 4 || -15 > -10
true
!(-4  4) && (1 > 2 || 1 < 2)
true

1.5.2.1. Chained comparisons#

In Julia, comparisons can be arbitrarily chained:

1 < 2 <= 2 < 3 == 3 > 2 >= 1 == 1 < 3 != 5
true

This syntax is convenient to, for example, determine if a variable is in a given range:

x = -2.3
-3  x  4
true

1.5.2.2. The if-statement#

We are now ready to define the if statement for conditional execusion. It has the following syntax:

    if x < y
        println("x is less than y")
    elseif x > y
        println("x is greater than y")
    else
        println("x is equal to y")
    end
  • First x < y is evaluated, if true then the corresponding block is evaluated

  • Otherwise, x > y is evaluated, if true then the corresponding block is evaluated

  • If neither expression is true, the else block is evaluated

  • The elseif and else blocks are optional, and elseif can be repeated

# Examples of if-else statements

function check_x(x)
    if 0  x  10
        println("x is between 0 and 10")
    elseif !(x < 0)
        println("x is not between 0 and 10, and x is not negative (meaning x > 10)")
    else
        println("x is not between 0 and 10, and x is not not negative (meaning x is negative)")
    end
end
check_x (generic function with 1 method)
check_x(π)
x is between 0 and 10
check_x(10.000001)
x is not between 0 and 10, and x is not negative (meaning x > 10)
check_x(2^63)        # trick question - causes Int64 overflow
x is not between 0 and 10, and x is not not negative (meaning x is negative)

1.5.2.3. Short-circuit evaluation#

In a series of boolean expressions connected by these operators, only the minimum number of expressions are evaluated as are necessary to determine the final boolean value of the entire chain. For example:

  • In the expression a && b, the subexpression b is only evaluated if a evaluates to true

  • In the expression a || b, the subexpression b is only evaluated if a evaluates to false

This allows for convenient error handling, for example the following code will never take the square root of a negative number (which would cause an error):

x = -3
if x  0 && sqrt(x) < 10
    # do something
else
    println("Sorry, need a non-negative number with square root strictly less than 10")
end
Sorry, need a non-negative number with square root strictly less than 10

1.5.3. Example: Fibonacci, handling special cases#

One common use for if-statements is the handling of special cases. For example, the Fibonacci sequence is defined by \(F_0 = 0\), \(F_1 = 1\), and the recurrence relation

\[ F_{k} = F_{k-1} + F_{k-2}\qquad \text{for }k\ge 2. \]

In the implementation below, the special cases \(F_0\) and \(F_1\) are handled separately, and for all other numbers the recurrence relation is used.

function fibonacci(n)
    # First special cases
    if n == 0
        return 0
    elseif n == 1
        return 1
    else
        # General case n ≥ 2 - initialize and iterate
        Fkold = 0
        Fk = 1
        for k = 2:n
            Fknew = Fk + Fkold
            # Swap previous values
            Fkold = Fk
            Fk = Fknew
        end
        return Fk
    end
end
fibonacci (generic function with 1 method)
fibonacci(10)
55
fibonacci(1000)   # Again be careful with overflow of Int64, but this one is OK
817770325994397771

1.5.4. Example: Multiples of 3 and 5#

Project Euler, Problem 1:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

Let us create a general function that solves the problem for numbers below a given value n. Note that we can use the remainder operator % to check if a number is a multiple of another number.

function PE1(n)
    s = 0
    for i = 1:n-1
        if i % 3 == 0 || i % 5 == 0
            s += i
        end
    end
    s
end
PE1 (generic function with 1 method)

We can now confirm that our function gives the right answer for the example provided:

PE1(10)
23

and we can answer the main question:

PE1(1000)
233168