Question 1

Write the following functions. Some questions use specific data types that are defined below.

data Grade = N | PS | P | CR | D | HD
  deriving (Show, Eq)

data TrafficLight = Red | Yellow | Green
  deriving (Show, Eq)

data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
  deriving (Show, Eq)

1) A case function that returns the driving instruction based on what the traffic light shows.

traffic :: TrafficLight -> String
traffic light = case light of
  Red     -> "Stop"
  Yellow  -> "Slow Down"
  Green   -> "Go"

2) A guard function that takes your grade as a number and returns the appropriate Grade value.

grading :: Int -> Grade
grading score
  | score > 80  = HD
  | score > 70  = D
  | score > 60  = Cr
  | score > 50  = P
  | score == 50 = PS
  | otherwise   = N

3) A case function that takes a day of the week and returns where the COMP1100 lecture is that day.

lectureLocation :: Day -> "String"
lectureLocation day = case day of
  Monday    -> error "No Lecture on Monday"
  Tuesday   -> error "No Lecture on Tuesday"
  Wednesday -> "Llewellyn Hall"
  Thursday  -> "Llewellyn Hall"
  Friday    -> "Manning Clark Hall"

4) A guard function that checks is a positive number is even and if so returns "This number is even!".

isEvenString :: Int -> String
isEvenString i
  | i % 2 == 0  = "This number is even!"
  | otherwise   = "This number is odd!"

5) A guard function that takes two numbers and determines if both, one or none are even. (Remeber that && means ‘AND’ and ```||`` means ‘OR’ in Haskell!)

isEvenTwo :: Int -> Int -> Int
isEvenTwo x y
  | x % 2 == 0 && y % 2 == 0  = 2
  | x % 2 == 0 || y % 2 == 0  = 1
  | otherwise                 = 0

6) A case function that takes a character and checks if it is vowel or not.

isVowel :: Char -> Bool
isVowel char = case char of
  'a' -> True
  'e' -> True
  'i' -> True
  'o' -> True
  'u' -> True
  _   -> False

Question 2

Let’s make a Scissors-Paper-Rock game in Haskell! To begin, we define our data types:

data Move = Paper | Rock | Scissors deriving (Eq, Show)
data Result = Win | Draw | Lose deriving (Eq, Show)

These should both be pretty self-explanatory. Now, consider the following function:

beats :: Move -> Move
beats move =
    | move == Paper     = Scissors
    | move == Rock      = Paper
    | move == Scissors  = Rock

This function will return the move that beats the input move. We can also write this function using Cases:

beats :: Move -> Move
beast move = case move of
    Rock      -> Paper
    Paper     -> Scissors
    Scissors  -> Rock

Both versions of beats will produce the same output, however it is clear that using a case statement over guards leads to more easily readable code. Whenever you need a decision-making function, always consider whether you should use a Case or a Guard: always try to choose the option that leads to the easiest, simplest and cleanest code.

With this in mind, it is now your turn to choose which method to use as we complete the Scissors-Paper-Rock game. Finish the following function: it takes two moves as input, your move and your opponents move, and tells you the result:

score :: Move -> Move -> Result
score myMove opponentMove ...

Remember - think carefully whether you want to you Cases or Guards. (Hint: Use the beats function!)

The function can be written using Guards or Cases:

score :: Move -> Move -> Result
score myMove opponentMove
  | beats opponentMove == myMove  = Win
  | beats myMove == opponentMove  = Lose
  | myMove == opponentMove        = Draw
  | otherwise                     = error "Unexpected Result Achieved"

score :: Move -> Move -> Result
score myMove opponentMove = case myMove of
  opponentMove        -> Draw
  beats opponentMove  -> Win
  _                   -> Lose

However, the Guard function is more appropriate. Although it is longer, it is much more clear what the function is doing: the readability is better. Additionally, we can generate an error when something strange happens.