En garde!
Hi! I’m a shouty man.1 Do you miss the switch
statement from languages such as C, Java, and JavaScript? Or the match
statement from Python? Tired of writing (or reading) nested conditional expressions like the following?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Text.Printf
-- | Relationship between two integers.
main = do
putStrLn "Enter first integer."
a <- getLine
putStrLn "Enter second integer."
b <- getLine
let k = read a :: Integer
let n = read b :: Integer
let relation =
if k < n
then "less than"
else if k > n
then "greater than"
else "equal to"
Say no more. What if I tell you that you can get rid of nested conditional expressions? How would you like to organize your nested conditional expressions according to the cases or conditions you want to match? Curious? Read on.
Guarded equations
A conditional expression can be replaced with a guarded equation. To understand what a guarded equation can do for you, recall the function maxInt
from the section Multiple parameters:
1
2
3
4
5
6
-- | The maximum of two integers.
maxInt :: Integer -> Integer -> Integer
maxInt x y =
if x > y
then x
else y
Here is the same function, but written as a guarded equation:
1
2
3
4
5
-- | The maximum of two integers.
maxIntg :: Integer -> Integer -> Integer
maxIntg x y
| x > y = x
| otherwise = y
And here is the guarded equation translated to a mathematical expression:
\[\text{max}(x,\, y) = \begin{cases} x,& \text{if } x > y,\\[8pt] y,& \text{otherwise}. \end{cases}\]A guarded equation is a function that follows the format
1
2
3
4
5
funcName a b ...
| conditional_1 = result_1
| conditional_2 = result_2
...
| otherwise = result_n
First, we have the name funcName
of the guarded equation. We use the name of the guarded equation and pass parameters to it in the same way that we apply a function. A guarded equation is a function. The pipe symbol |
is read as “such that” and signifies the beginning of a guard. A guard is a conditional, an expression that evaluates to a boolean value. To the right of the guard is the equal sign =
, followed by the result corresponding to the guard. The line
1
| conditional_1 = result_1
can be read as, “The value of the function is such that if conditional_1
holds True
, then the output is result_1
.” The other lines have similar interpretation. The final line of a guarded equation is
1
| otherwise = result_n
Like the switch
statement (and the label default
) in some languages, Haskell does not require the guard otherwise
in the final line of your guarded equation. However, do bear in mind that the guard otherwise
is defined as otherwise = True
and serves as a catch-all for every case not handled by the previous guards. Use the guard otherwise
to handle the default output of your guarded equation.
Body mass index
The body mass index (BMI) is a quick and easy way to assess a person’s health in terms of their weight and height. The formula is given as
\[\text{BMI} = \frac{ \text{weight} }{ \text{height} \times \text{height} }\]where the weight (or mass) is measured in kilograms and the height is measured in metres. According to the US Centers for Disease Control and Prevention (CDC), an adult of 20 years or older can be categorized as follows according to their BMI.
BMI | Status |
---|---|
below 18.5 | underweight |
18.5–24.9 | healthy weight |
25.0–29.9 | overweight |
30.0 or above | obese |
The information in the above table is translated to a guarded equation as follows.
1
2
3
4
5
6
7
8
-- | The weight status of an adult 20 years or older, based on their BMI.
weightStatus :: Double -> String
weightStatus bm
| bm < 18.5 = "underweight"
| (18.5 <= bm) && (bm <= 24.9) = "healthy weight"
| (25.0 <= bm) && (bm <= 29.9) = "overweight"
| bm >= 30.0 = "obese"
| otherwise = "unknown BMI or weight status"
Java example
Different coffee beverages are best served with cups of various sizes. According to this website, the cup sizes corresponding to some drinks are as given below.
Drink | Cup size |
---|---|
espresso | 56–85 ml |
cappuccino | 142–170 ml |
latte | 227–426 ml |
frappuccino | 312–426 ml |
Your mission, should you choose to accept it, is to write a function that takes the name of a caffeinated drink and outputs the corresponding cup size. Did you decline? No problem. Below is a translation of the above table to Haskell code.
1
2
3
4
5
6
7
8
-- | The cup size corresponding to a coffee beverage.
cupSize :: String -> String
cupSize drink
| drink == "espresso" = "56--85 ml"
| drink == "cappuccino" = "142--170 ml"
| drink == "latte" = "227--426 ml"
| drink == "frappuccino" = "312--426 ml"
| otherwise = "unknown drink"
Exercises
Exercise 1. Write a guarded equation to determine the minimum of two integers.
Exercise 2. Rewrite the program nested.hs
by using guarded equation.
Exercise 3. The sign or signum function is defined as follows:
\[\text{sgn}(x) = \begin{cases} -1,& \text{if } x < 0,\\[8pt] 0,& \text{if } x = 0,\\[8pt] 1,& \text{if } x > 0. \end{cases}\]Implement the sign function as a Haskell guarded equation.
Exercise 4. Repeat the daily saying exercise, but use guarded equation.
Exercise 5. The triangular function is defined as:
\[\text{tri}(x) = \begin{cases} 1 - |x|,& \text{if } |x| < 1,\\[8pt] 0,& \text{otherwise}. \end{cases}\]Repeat the exercise on absolute value, but implement a guarded equation. Then implement the triangular function as a guarded equation.
Exercise 6. The size of a college in the USA can be categorized according to the number of students the college has. Refer to the table below.
Students | Size |
---|---|
less than 5,000 | small |
5,000 to 15,000 | medium |
greater than 15,000 | large |
Implement the above table as a guarded equation.
Exercise 7. In the USA, the size of woman’s clothing can be designated by a number between 0 and 22. We can assign the size to a category as given below.
Numeric size | Category |
---|---|
0–2 | XS |
4–6 | S |
8–10 | M |
12–14 | L |
16–18 | XL |
20 | XXL |
22 | XXXL |
Implement the above table as a guarded equation.
Exercise 8. A quadratic equation can be written in the general form
\[ax^2 + bx + c = 0\]where $a,b,c$ are real numbers with $a \neq 0$. The solution(s) to a quadratic equation depends on the value of the discriminant $\Delta = b^2 - 4ac$. In particular, we have the following cases:
- If $\Delta > 0$, then we have two distinct real solutions.
- If $\Delta = 0$, then we have a repeated real solution.
- If $\Delta < 0$, then we have two distinct complex solutions.
Write a guarded equation to determine the type of solutions of a given quadratic equation.
Exercise 9. Write a Haskell function that outputs the body mass index given an adult’s weight (kilogram) and height (metre). Use your implementation to test the function weightStatus
from the section Body mass index.
Exercise 10. Using guarded equation, write a function for each of the following.
- The maximum of three integers.
- The minimum of three integers.
- The middle of three integers.
A recurring salesman from Horrible History. See him in action here. ↩