diff --git a/test1/README.md b/test1/README.md new file mode 100644 index 0000000..066309a --- /dev/null +++ b/test1/README.md @@ -0,0 +1,225 @@ +# Test 1 + +## Marking table + +The exercises are defined so that it is hard to get a first-class mark. + +| Mark | Cut-off | +| ------------ | ------------------ | +| 1st | 28 marks and above | +| upper second | 24-27 marks | +| lower second | 20-23 marks | +| third | 16-19 marks | +| fail | 0-15 marks | + +All questions have equal weight, with eight marks each, with a total of 40 +marks. + +## Preparation + +* The test must be completed on JupyterLab. +* Run `git pull` on JupyterLab to make sure you have the latest version of the + course repository. +* We have defined some helper functions for your in the `Types.hs` file. You are + encouraged to make use of them in your solutions. +* Do __not__ modify either the file `Types.hs` or the file + `Test1-Template.hs`. +* Copy the file `Test1-Template.hs` to a new file called + `Test1.hs` and write your solutions in `Test1.hs`. + + __Don't change the header of this file, including the module declaration, and, + moreover, don't change the type signature of any of the given functions for + you to complete.__ + + __If you do make changes, then we will not be able to mark your submission and + hence it will receive zero marks!__ +* Solve the exercises below in the file `Test1.hs`. + +## Submission procedure + +* If your submission doesn't compile or fails to pass the presubmit script on + JupyterLab, it will get zero marks. +* Run the presubmit script provided to you on your submission from Jupyter by + running `./presubmit.sh Test1` in the terminal (in the same folder as + your submission). +* This will check that your submission is in the correct format. +* If it is, submit it on Canvas. +* Otherwise fix and repeat the presubmission procedure. + +## Plagiarism + +Plagiarism will not be tolerated. Copying and contract cheating have led to full +loss of marks, and even module or degree failure, in the past. + +You will need to sign a declaration on Canvas, before submission, that you +understand the [rules](/README.md#plagiarism) and are abiding by them, in order +for your submission to qualify. + +## Background material + +- Each question has some **Background Material**, an **Implementation Task** and + possibly some **Examples**. +- Read this material first, then implement the requested function. +- The corresponding type appears in the file `Test1-Template.hs` (to be + copied by you). +- Replace the default function implementation of `undefined` with your own + function. + +## More Rules + +* This is an open book test. +* You may consult your own notes, the course materials, any of the recommended + books or [Hoogle](https://hoogle.haskell.org/). +* Feel free to write helper functions whenever convenient. +* All the exercises may be solved without importing additional modules. Do not + import any modules, as it may interfere with the marking. + +## Submission Deadline + +* The official submission deadline is 1pm. +* If you are provided extra time by the Welfare office then your submission + deadline is 1:30pm or 2:00pm. +* Submissions close 10 minutes after your deadline, and late submissions have 5% + penalty + +## Question 1 — Even majority (**8 marks**) + +**Task** Write the following function `evenMajority`, that takes a list of +integers, and tells whether more than half of them are even. + +```haskell +evenMajority :: [Int] -> Bool +evenMajority ns = undefined +``` + +## Question 2 — 5-smooth numbers (**8 marks**) + +A 5-smooth number is an integer which has no prime factor larger than 5. For an +integer N, we define S(N) as the set of 5-smooth numbers less than or equal to +N. For example S(20) = {1,2,3,4,5,6,8,9,10,12,15,16,18,20} + +**Task** Define the following function `get5SmoothNumbers`, that gives a list of +all 5-smooth numbers less than or equal to a given number. + +```haskell +get5SmoothNumbers :: Int -> [Int] +get5SmoothNumbers k = undefined +``` + +Examples: + +```hs +*Test1> get5SmoothNumbers 25 +[1,2,3,4,5,6,8,9,10,12,15,16,18,20,24,25] + +*Test1> get5SmoothNumbers 50 +[1,2,3,4,5,6,8,9,10,12,15,16,18,20,24,25,27,30,32,36,40,45,48,50] +``` + +## Question 3 — Train stops (**8 marks**) + +Consider the following type of train stops on the West Midlands Railway line, +from Redditch to Birmingham New Street. + +```haskell +data TrainStop = BirminghamNewStreet + | FiveWays + | University + | SellyOak + | Bournville + | KingsNorton + | Northfield + | Longbridge + | BarntGreen + | Alvechurch + | Redditch + deriving (Eq, Show) +``` + +We define the function `theStopAfter` on this type, which encodes the +information of which stop comes immediately after which stop. + +```haskell +theStopAfter :: TrainStop -> TrainStop +theStopAfter Redditch = Alvechurch +theStopAfter Alvechurch = BarntGreen +theStopAfter BarntGreen = Longbridge +theStopAfter Longbridge = Northfield +theStopAfter Northfield = KingsNorton +theStopAfter KingsNorton = Bournville +theStopAfter Bournville = SellyOak +theStopAfter SellyOak = University +theStopAfter University = FiveWays +theStopAfter FiveWays = BirminghamNewStreet +theStopAfter BirminghamNewStreet = undefined +``` + +Note that the function is undefined on `BirminghamNewStreet` because that is the +last possible stop on this line. You should ensure that this function is never +called on `BirminghamNewStreet`, because the program will crash if you do that. + +**Task** Define the following function `comesBefore`, using the given +`theStopAfter` function, such that `comesBefore s1 s2` is `True` if and only if +`s1` is a stop preceding stop `s2`. + +```haskell +comesBefore :: TrainStop -> TrainStop -> Bool +comesBefore s1 s2 = undefined +``` + +Some examples: + +```hs +*Test1> comesBefore University BirminghamNewStreet +True +*Test1> comesBefore Bournville FiveWays +True +*Test1> comesBefore BirminghamNewStreet University +False +*Test1> comesBefore University KingsNorton +False +*Test1> comesBefore University University +False +``` + +## Question 4 — Repeated applications of a function (**8 marks**) + +**Task** Write a function `countApplications` + +```haskell +countApplications :: (a -> a) -> (a -> Bool) -> a -> Int +countApplications f p x = undefined +``` + +that takes + + 1. a function `f :: a -> a`, + 1. a termination condition `p :: a -> Bool`, and + 1. an input `x :: a`, + +and counts the number of times that the function `f` must be repeatedly applied +to `x` until the output satisfies the condition `p`. + +Here is an example of how to use this function: + +```hs +*Test1> countApplications (\n -> n `div` 2) odd 8 +3 +*Test1> countApplications (\n -> n `div` 2) odd 10 +1 +``` + +We will only test your implementation of `countApplications` on functions that +do terminate with respect to the termination condition. + +## Question 5 — Higher order functions (**8 marks**) + +**Task** Write a function `f` of the following type + +```haskell +f :: (a -> a -> r) -> ((a -> r) -> a) -> r +f g h = undefined +``` + +The function should terminate for all terminating inputs. Your solution should +not use recursion or `undefined`. diff --git a/test1/Test1.hs b/test1/Test1.hs new file mode 100644 index 0000000..97f3511 --- /dev/null +++ b/test1/Test1.hs @@ -0,0 +1,59 @@ +-- setting the "warn-incomplete-patterns" flag asks GHC to warn you +-- about possible missing cases in pattern-matching definitions +{-# OPTIONS_GHC -fwarn-incomplete-patterns #-} + +-- see https://wiki.haskell.org/Safe_Haskell +{-# LANGUAGE Safe #-} + +module Test1 ( evenMajority + , get5SmoothNumbers + , comesBefore + , countApplications + , f + ) where + +import Types + +{- QUESTION 1 -} + +_countEvens :: [Int] -> Int +_countEvens ns = foldl (\acc x -> if even x then acc + 1 else acc) 0 ns + +evenMajority :: [Int] -> Bool +evenMajority ns = let half = (length ns) `div` 2 in (_countEvens ns) > half + +{- QUESTION 2 -} + +_is5Smooth :: Int -> Bool +_is5Smooth n = foldl (\acc x -> acc && x <= 5) True (primeFactors n) + +get5SmoothNumbers :: Int -> [Int] +get5SmoothNumbers n = filter _is5Smooth [1..n] + +{- QUESTION 3 -} + +_getStationsBefore :: TrainStop -> Maybe TrainStop -> [TrainStop] +_getStationsBefore finalStop Nothing = _getStationsBefore finalStop (Just Redditch) +_getStationsBefore finalStop (Just previousStop) | finalStop == previousStop = [] + | otherwise = previousStop:(_getStationsBefore finalStop (Just (theStopAfter previousStop))) + +comesBefore :: TrainStop -> TrainStop -> Bool +comesBefore s1 s2 = not (foldl (\acc x -> acc && x /= s1) True (_getStationsBefore s2 Nothing)) + +{- QUESTION 4 -} + +_countApplications :: (a -> a) -> (a -> Bool) -> a -> Int -> Int +_countApplications f p acc tries | (p acc) = tries + | otherwise = _countApplications f p (f acc) (tries + 1) + +countApplications :: (a -> a) -> (a -> Bool) -> a -> Int +countApplications f p x = _countApplications f p x 0 + +{- QUESTION 5 -} + +_cloneArgument :: (a -> a -> r) -> (a -> r) +_cloneArgument f = (\x -> f x x) + +f :: (a -> a -> r) -> ((a -> r) -> a) -> r +f g h = let i = (_cloneArgument g) in + g (h i) (h i) diff --git a/test1/Types.hs b/test1/Types.hs new file mode 100644 index 0000000..cdf28c3 --- /dev/null +++ b/test1/Types.hs @@ -0,0 +1,56 @@ +-- setting the "warn-incomplete-patterns" flag asks GHC to warn you +-- about possible missing cases in pattern-matching definitions +{-# OPTIONS_GHC -fwarn-incomplete-patterns #-} + +-- see https://wiki.haskell.org/Safe_Haskell +{-# LANGUAGE Safe #-} + +--------------------------------------------------------------------------------- +-------------------------- DO **NOT** MODIFY THIS FILE -------------------------- +--------------------------------------------------------------------------------- + +module Types where + +-- Question 2 + +factors :: Int -> [Int] +factors n = [ k | k <- [1..n] , n `mod` k == 0 ] + +isPrime :: Int -> Bool +isPrime n = factors n == [1, n] + +primeFactors :: Int -> [Int] +primeFactors n = [x | x <- [1..n], n `mod` x == 0 && isPrime x] + +-- Question 3 + +data TrainStop = BirminghamNewStreet + | FiveWays + | University + | SellyOak + | Bournville + | KingsNorton + | Northfield + | Longbridge + | BarntGreen + | Alvechurch + | Redditch + deriving (Eq, Show) + +theStopAfter :: TrainStop -> TrainStop +theStopAfter Redditch = Alvechurch +theStopAfter Alvechurch = BarntGreen +theStopAfter BarntGreen = Longbridge +theStopAfter Longbridge = Northfield +theStopAfter Northfield = KingsNorton +theStopAfter KingsNorton = Bournville +theStopAfter Bournville = SellyOak +theStopAfter SellyOak = University +theStopAfter University = FiveWays +theStopAfter FiveWays = BirminghamNewStreet +theStopAfter BirminghamNewStreet = undefined + +-- Question 4 + +divideBy2 :: Int -> Int +divideBy2 n = n `div` 2 diff --git a/test1/presubmit.sh b/test1/presubmit.sh new file mode 100644 index 0000000..9d5a4de --- /dev/null +++ b/test1/presubmit.sh @@ -0,0 +1,103 @@ +#!/bin/sh + +if [ "$1" = "" ] +then + echo "You forgot to add the assignment name, e.g. 'Assessed1'." + echo "Please run the script again with the right argument." + exit 1 +fi + +if ! [ -f "$1.hs" ] +then + echo "File '$1.hs' not found." + echo "Are you in the correct directory?" + exit 1 +fi + +echo "Trying to compile your submission..." + +# Create temporary directory +temp_dir=$(mktemp -d) + +ghc $1.hs -odir $temp_dir -hidir $temp_dir + +if [ $? -ne 0 ] +then + echo "" + echo "Your file '$1.hs' did not compile." + echo "Please fix it before submitting." + exit 1 +fi + +if ! [ -f "$temp_dir/$1.o" ] +then + echo "" + echo "The module name in '$1.hs' does match not the filename '$1'." + echo "Please make sure you that" + echo -e "\t(i) your file is called something like 'TestX.hs'" + echo -e "\t(ii) you did not change the top of the template" + echo "and try again." + exit 1 +fi + +ghc -XSafe $1.hs -odir $temp_dir -hidir $temp_dir + +if [ $? -ne 0 ] +then + echo "" + echo "Your file did not compile with '-XSafe.'" + echo "Did you remove '{-# LANGUAGE Safe #-}' from the template?" + exit 1 +fi + +# Create file for ensuring type signatures have not been modified + +cat >> $temp_dir/Signatures.hs << 'END' +{-# LANGUAGE Safe #-} +module Signatures where + +import Types +import Test1 + +{- QUESTION 1 -} + +evenMajorityTest :: [Int] -> Bool +evenMajorityTest = evenMajority + +{- QUESTION 2 -} + +get5SmoothNumbersTest :: Int -> [Int] +get5SmoothNumbersTest = get5SmoothNumbers + +{- QUESTION 3 -} + +comesBeforeTest :: TrainStop -> TrainStop -> Bool +comesBeforeTest = comesBefore + +{- QUESTION 4 -} + +countApplicationsTest :: (a -> a) -> (a -> Bool) -> a -> Int +countApplicationsTest = countApplications + +{- QUESTION 5 -} + +fTest :: (a -> a -> r) -> ((a -> r) -> a) -> r +fTest = f +END + +ghc -XSafe $temp_dir/Signatures.hs -odir $temp_dir -hidir $temp_dir + +if [ $? -ne 0 ] +then + echo "" + echo "Your file did not compile with the correct type signatures." + echo "Did you modify the type signatures from the template?" + exit 1 +fi + +echo "" +echo "All checks passed." +echo "You are ready to submit!" + +# Cleanup temporary directory +rm -r $temp_dir