Files
2022-08-08 11:48:33 +03:00

230 lines
7.9 KiB
Haskell

-- Welcome to the first exercise set of part 2 of the Haskell Mooc!
-- Edit this file according to the instructions, and check your
-- answers with
--
-- stack runhaskell Set9aTest.hs
--
-- You can also play around with your answers in GHCi with
--
-- stack ghci Set9a.hs
module Set9a where
import Data.Char
import Data.List
import Data.Ord
import Mooc.Todo
------------------------------------------------------------------------------
-- Ex 1: Implement a function workload that takes in the number of
-- exercises a student has to finish, and another number that counts
-- the number of hours each exercise takes.
--
-- If the total number of hours needed for all exercises is over 100,
-- return "Holy moly!" if it is under 10, return "Piece of cake!".
-- Otherwise return "Ok."
workload :: Int -> Int -> String
workload nExercises hoursPerExercise = todo
------------------------------------------------------------------------------
-- Ex 2: Implement the function echo that builds a string like this:
--
-- echo "hello!" ==> "hello!, ello!, llo!, lo!, o!, !, "
-- echo "ECHO" ==> "ECHO, CHO, HO, O, "
-- echo "X" ==> "X, "
-- echo "" ==> ""
--
-- Hint: use recursion
echo :: String -> String
echo = todo
------------------------------------------------------------------------------
-- Ex 3: A country issues some banknotes. The banknotes have a serial
-- number that can be used to check if the banknote is valid. For a
-- banknote to be valid, either
-- * the third and fifth digits need to be the same
-- * or the fourth and sixth digits need to be the same
--
-- Given a list of bank note serial numbers (strings), count how many
-- are valid.
countValid :: [String] -> Int
countValid = todo
------------------------------------------------------------------------------
-- Ex 4: Find the first element that repeats two or more times _in a
-- row_ in the input list. Return a Nothing value if no element repeats.
--
-- Examples:
-- repeated [1,2,3] ==> Nothing
-- repeated [1,2,2,3,3] ==> Just 2
-- repeated [1,2,1,2,3,3] ==> Just 3
repeated :: Eq a => [a] -> Maybe a
repeated = todo
------------------------------------------------------------------------------
-- Ex 5: A laboratory has been collecting measurements. Some of the
-- measurements have failed, so the lab is using the type
-- Either String Int
-- to track the measurements. A Left value represents a failed measurement,
-- while a Right value represents a successful one.
--
-- Compute the sum of all successful measurements. If there are
-- successful measurements, return the sum wrapped in a Right, but if
-- there are none, return Left "no data".
--
-- Examples:
-- sumSuccess [Right 1, Left "it was a snake!", Right 3]
-- ==> Right 4
-- sumSuccess [Left "lab blew up", Left "I was sick"]
-- ==> Left "no data"
-- sumSuccess []
-- ==> Left "no data"
sumSuccess :: [Either String Int] -> Either String Int
sumSuccess = todo
------------------------------------------------------------------------------
-- Ex 6: A combination lock can either be open or closed. The lock
-- also remembers a code. A closed lock can only be opened with the
-- right code. The code of an open lock can be changed.
--
-- Implement a datatype Lock and the functions isOpen, open, lock,
-- changeCode and the constant aLock as instructed below.
--
-- Examples:
-- isOpen aLock ==> False
-- isOpen (lock aLock) ==> False
-- isOpen (open "1234" aLock) ==> True
-- isOpen (lock (open "1234" aLock)) ==> False
-- isOpen (open "1235" aLock) ==> False
-- isOpen (lock (open "1235" aLock)) ==> False
-- isOpen (open "1234" (changeCode "0000" aLock)) ==> True
-- isOpen (open "0000" (changeCode "0000" aLock)) ==> False
-- isOpen (open "0000" (lock (changeCode "0000" (open "1234" aLock)))) ==> True
-- isOpen (open "1234" (lock (changeCode "0000" (open "1234" aLock)))) ==> False
data Lock = LockUndefined
deriving Show
-- aLock should be a locked lock with the code "1234"
aLock :: Lock
aLock = todo
-- isOpen returns True if the lock is open
isOpen :: Lock -> Bool
isOpen = todo
-- open tries to open the lock with the given code. If the code is
-- wrong, nothing happens.
open :: String -> Lock -> Lock
open = todo
-- lock closes a lock. If the lock is already closed, nothing happens.
lock :: Lock -> Lock
lock = todo
-- changeCode changes the code of an open lock. If the lock is closed,
-- nothing happens.
changeCode :: String -> Lock -> Lock
changeCode = todo
------------------------------------------------------------------------------
-- Ex 7: Here's a type Text that just wraps a String. Implement an Eq
-- instance for Text that ignores all white space (space characters
-- and line returns).
--
-- Hint: Data.Char.isSpace
--
-- Examples
-- Text "abc" == Text "abc" ==> True
-- Text "a bc" == Text "ab c\n" ==> True
-- Text "abc" == Text "abcd" ==> False
-- Text "a bc" == Text "ab d\n" ==> False
data Text = Text String
deriving Show
------------------------------------------------------------------------------
-- Ex 8: We can represent functions or mappings as lists of pairs.
-- For example the list [("bob",13),("mary",8)] means that "bob" maps
-- to 13 and "mary" maps to 8.
--
-- Implement _composition_ for mappings like this. You compose two
-- mappings by looking up each result of the first mapping in the
-- second mapping.
--
-- You may assume there are no repeated first elements of tuples in
-- the argument lists, that is.
--
-- The ordering of the output doesn't matter.
--
-- Hint: remember the function `lookup` from Prelude?
--
-- Note! The order of arguments to `compose` is the other way around
-- compared to e.g. (.): `compose f g` should apply `f` first, then
-- `g`, but `f.g` applies `g` first, then `f`.
--
-- Examples:
-- composing two mappings of size 1:
-- compose [("a",1)] [(1,True)]
-- ==> [("a",True)]
-- nonmatching mappings get ignored:
-- compose [("a",1),("b",2)] [(3,False),(4,True)]
-- ==> []
-- a more complex example: note how "omicron" and "c" are ignored
-- compose [("a","alpha"),("b","beta"),("c","gamma")] [("alpha",1),("beta",2),("omicron",15)]
-- ==> [("a",1),("b",2)]
compose :: (Eq a, Eq b) => [(a,b)] -> [(b,c)] -> [(a,c)]
compose = todo
------------------------------------------------------------------------------
-- Ex 9: Reorder a list using a list of indices.
--
-- You are given a list of indices (numbers from 0 to n) and an input
-- list (of length n). Each index in the index list tells you where to
-- place the corresponding element from the input list in the output
-- list.
--
-- For example, if the 3rd element of the index list is 7, and the 3rd
-- element of the input list is 'a', the output list should have 'a'
-- at index 7.
--
-- (The index lists discussed in this exercise correspond to permutations in
-- math. In fact, permutations can be multiplied which is a special case of
-- the compose function in the previous exercise. For more information on
-- permutations, see https://en.wikipedia.org/wiki/Permutation)
--
-- Examples:
-- permute [0,1] [True, False] ==> [True, False]
-- permute [1,0] [True, False] ==> [False, True]
-- permute [0,1,2,3] "hask" ==> "hask"
-- permute [2,0,1,3] "hask" ==> "ashk"
-- permute [1,2,3,0] "hask" ==> "khas"
-- permute [2, 1, 0] (permute [2, 1, 0] "foo") ==> "foo"
-- permute [1, 0, 2] (permute [0, 2, 1] [9,3,5]) ==> [5,9,3]
-- permute [0, 2, 1] (permute [1, 0, 2] [9,3,5]) ==> [3,5,9]
-- permute ([1, 0, 2] `multiply` [0, 2, 1]) [9,3,5] ==> [5,9,3]
-- permute ([0, 2, 1] `multiply` [1, 0, 2]) [9,3,5] ==> [3,5,9]
-- A type alias for index lists.
type Permutation = [Int]
-- Permuting a list with the identity permutation should change nothing.
identity :: Int -> Permutation
identity n = [0 .. n - 1]
-- This function shows how permutations can be composed. Do not edit this
-- function.
multiply :: Permutation -> Permutation -> Permutation
multiply p q = map (\i -> p !! (q !! i)) (identity (length p))
permute :: Permutation -> [a] -> [a]
permute = todo