mirror of
https://github.com/iritmaximus/haskell.git
synced 2026-10-12 19:13:39 +03:00
230 lines
7.9 KiB
Haskell
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
|