add Set4a, Set4b

This commit is contained in:
Joel Kaasinen
2020-05-06 09:17:16 +03:00
parent 748898b4ae
commit 32b7ab3c6c
4 changed files with 568 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
-- Exercise set 4a:
--
-- * using type classes
-- * working with lists
--
-- Type classes you'll need
-- * Eq
-- * Ord
-- * Num
-- * Fractional
--
-- Useful functions:
-- * maximum
-- * minimum
-- * sort
module Set4a where
import Mooc.Todo
import Data.List
import Data.Ord
import qualified Data.Map as Map
import Data.Array
------------------------------------------------------------------------------
-- Ex 1: implement the function allEqual which returns True if all
-- values in the list are equal.
--
-- Examples:
-- allEqual [] ==> True
-- allEqual [1,2,3] ==> False
-- allEqual [1,1,1] ==> True
--
-- PS. check out the error message you get with your implementation if
-- you remove the Eq a => constraint from the type!
allEqual :: Eq a => [a] -> Bool
allEqual xs = todo
------------------------------------------------------------------------------
-- Ex 2: implement the function distinct which returns True if all
-- values in a list are different.
--
-- Hint: a certain function from the lecture material can make this
-- really easy for you.
--
-- Examples:
-- distinct [] ==> True
-- distinct [1,1,2] ==> False
-- distinct [1,2] ==> True
distinct :: Eq a => [a] -> Bool
distinct = todo
------------------------------------------------------------------------------
-- Ex 3: implement the function middle that returns the middle value
-- (not the smallest or the largest) out of its three arguments.
--
-- The function should work on all types in the Ord class. Give it a
-- suitable type signature.
--
-- Examples:
-- middle 'b' 'a' 'c' ==> 'b'
-- middle 1 7 3 ==> 3
middle = todo
------------------------------------------------------------------------------
-- Ex 4: return the range of an input list, that is, the difference
-- between the smallest and the largest element.
--
-- Your function should work on all suitable types, like Float and
-- Int. You'll need to add _class constraints_ to the type of range.
--
-- It's fine if your function doesn't work for empty inputs.
--
-- Examples:
-- rangeOf [4,2,1,3] ==> 3
-- rangeOf [1.5,1.0,1.1,1.2] ==> 0.5
rangeOf :: [a] -> a
rangeOf = todo
------------------------------------------------------------------------------
-- Ex 5: given a list of lists, return the longest lists. If there
-- are multiple lists of the same length, return the string that has
-- the smallest _first element_.
--
-- (If multiple lists have the same length and same first element,
-- you can return any one of them.)
--
-- Give the longest function a suitable type.
--
-- Examples:
-- longest [[1,2,3],[4,5],[6]] ==> [1,2,3]
-- longest ["abc","def","axx"] ==> "def"
longest = todo
------------------------------------------------------------------------------
-- Ex 6: Implement the function incrementKey, that takes a list of
-- (key,value) pairs, and adds 1 to all the values that have the given key.
--
-- You'll need to add _class constraints_ to the type of incrementKey
-- to make the function work!
--
-- The function needs to be generic and handle all compatible types,
-- see the examples.
--
-- Examples:
-- incrementKey True [(True,1),(False,3),(True,4)] ==> [(True,2),(False,3),(True,5)]
-- incrementKey 'a' [('a',3.4)] ==> [('a',4.4)]
incrementKey :: k -> [(k,v)] -> [(k,v)]
incrementKey = todo
------------------------------------------------------------------------------
-- Ex 7: compute the average of a list of values of the Fractional
-- class.
--
-- There is no need to handle the empty list case.
--
-- Hint! since Fractional is a subclass of Num, you have all
-- arithmetic operations available
--
-- Hint! you can use the function fromIntegral to convert the list
-- length to a Fractional
average :: Fractional a => [a] -> a
average xs = todo
------------------------------------------------------------------------------
-- Ex 8: given a map from player name to score and two players, return
-- the name of the player with more points. If the players are tied,
-- return the name of the first player.
--
-- If a player doesn't exist in the map, you can assume they have 0 points.
--
-- Hint: Map.findWithDefault can make this simpler
--
-- Examples:
-- winner (fromList [("Bob",3470),("Jane",2130),("Lisa",9448)]) "Jane" "Lisa"
-- ==> "Lisa"
-- winner (Map.fromList [("Bob",5899),("Lisa",5899),("Mike",13607)]) "Bob" "Lisa"
-- ==> "Bob"
winner :: Map.Map String Int -> String -> String -> String
winner scores player1 player2 = todo
------------------------------------------------------------------------------
-- Ex 9: compute how many times each value in the list occurs. Return
-- the frequencies as a Map from value to Int.
--
-- Challenge 1: try using Map.alter for this
--
-- Challenge 2: use foldr to process the list
--
-- Example:
-- freqs [False,False,False,True]
-- ==> Map.fromList [(False,3),(True,1)]
freqs :: (Eq a, Ord a) => [a] -> Map.Map a Int
freqs xs = todo
------------------------------------------------------------------------------
-- Ex 10: recall the withdraw example from the course material. Write a
-- similar function, transfer, that transfers money from one account
-- to another.
--
-- However, the function should not perform the transfer if
-- * the from account doesn't exist,
-- * the to account doesn't exist
-- * or the from account doesn't have enough money.
--
-- Hint: there are many ways to implement this logic. Map.member or
-- Map.notMember might help.
--
-- Examples:
-- let bank = Map.fromList [("Bob",100),("Mike",50)]
-- transfer "Bob" "Mike" 20 bank
-- ==> fromList [("Bob",80),("Mike",70)]
-- transfer "Bob" "Mike" 120 bank
-- ==> fromList [("Bob",100),("Mike",50)]
-- transfer "Bob" "Lisa" 20 bank
-- ==> fromList [("Bob",100),("Mike",50)]
-- transfer "Lisa" "Mike" 20 bank
-- ==> fromList [("Bob",100),("Mike",50)]
transfer :: String -> String -> Int -> Map.Map String Int -> Map.Map String Int
transfer from to amount bank = todo
------------------------------------------------------------------------------
-- Ex 11: given an Array, find the index of the largest element. You
-- can assume the Array isn't empty.
--
-- Hint: check out Data.Array.indices or Data.Array.assocs
maxIndex :: (Ix i, Ord a) => Array i a -> i
maxIndex = todo
+204
View File
@@ -0,0 +1,204 @@
{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-}
module Set4aTest where
import Control.Monad
import Data.Array
import Data.List
import qualified Data.Map as Map
import Test.QuickCheck
import Mooc.Th
import Mooc.Test
import Set4a
main = score tests
tests = [(1,"allEqual",[ex1])
,(2,"distinct",[ex2_int, ex2_char])
,(3,"middle",[ex3_char, ex3_int])
,(4,"rangeOf",[ex4_int, ex4_integer, ex4_double])
,(5,"longest",[ex5_int, ex5_char])
,(6,"incrementKey",[ex6_bool_float, ex6_char_int])
,(7,"average",[ex7])
,(8,"winner",[ex8_winner, ex8_tie])
,(9,"freqs",[ex9_bool, ex9_int])
,(10,"transfer",[ex10_success, ex10_missing, ex10_notEnough])
,(11,"maxIndex",[ex11_bool, ex11_int])]
-- -- -- -- -- -- -- --
m_ex1_true example = forAll_ $ \(cnt,v) ->
$(testing [|allEqual (replicate cnt (v `asTypeOf` example))|]) (?==True)
m_ex1_false example = forAll_ $ \(v,NonEmpty vs) ->
not (elem (v `asTypeOf` example) vs) ==> $(testing [|allEqual (v:vs)|]) (?==False)
ex1 = conjoin [m_ex1_true True
,m_ex1_true 'a'
,m_ex1_false 'a'
,m_ex1_true (1::Int)
,m_ex1_false (1::Int)]
m_ex2 domain = forAllBlind (sublistOf domain) $ \xs ->
conjoin [forAllBlind (shuffle xs) $ \xs' -> $(testing [|distinct xs'|]) (?==True)
,not (null xs) ==> forAllBlind (shuffle (head xs:xs)) $ \xs' -> $(testing [|distinct xs'|]) (?==False)]
ex2_int = m_ex2 [1::Int,2,3,4]
ex2_char = m_ex2 ['a'..'z']
m_ex3 c =
let d = succ c
e = succ d
in forAllBlind (shuffle [c,d,e]) $ \[x,y,z] ->
$(testing [|middle x y z|]) (?==d)
ex3_char = forAllBlind (choose ('a','y')) m_ex3
ex3_int = forAllBlind (choose (0::Int,20)) m_ex3
genRangeOf delta = do
base <- arbitrary
vs <- listOf1 (choose (base, base+delta))
shuffle (base:base+delta:vs)
m_ex4 delta = forAllBlind (genRangeOf delta) $ \vs ->
$(testing [|rangeOf vs|]) (?==delta)
ex4_int = forAllBlind (choose (1::Int,10)) m_ex4
ex4_integer = forAllBlind (choose (1::Integer,10)) m_ex4
ex4_double = property $ do
delta <- elements [1::Int,3,5,7,9]
vs <- genRangeOf delta
return $ $(testing [|rangeOf (map conv vs)|]) (?~=conv delta)
where conv x = fromIntegral x / 2
ex5_int =
forAllBlind (choose (2,10)) $ \l ->
forAllBlind (vectorOf l (arbitrary :: Gen Int)) $ \ans ->
forAllBlind (listOf (choose (1,l - 1) >>= vector)) $ \rest ->
forAllBlind (shuffle (ans:rest)) $ \input ->
$(testing [|longest input|]) (?==ans)
ex5_char = property $ do
c <- choose ('a','k')
d <- choose ('a','z') `suchThat` (>c)
len <- choose (1,3)
crest <- vectorOf len (choose ('a','z'))
drest <- vectorOf len (choose ('a','z'))
wrong <- vectorOf len (choose ('a','z'))
input <- shuffle [c:crest, d:drest, wrong]
return $ $(testing [|longest input|]) (?==(c:crest))
ex6_bool_float =
property $ do
c <- choose (True,False)
let f i = fromIntegral i + 0.5
i <- f <$> choose (0,10::Int)
j <- f <$> choose (0,10::Int)
let inp = [(c,i),(c,j)]
return $ $(testing [|incrementKey c inp|]) (?==[(c,i+1),(c,j+1)])
ex6_char_int =
property $ do
is <- vectorOf 5 $ choose (0,20::Int)
ks <- vectorOf 5 $ choose ('a','g')
c <- choose ('a','h')
let inp = zip ks is
out = zipWith (\k i -> if k==c then (k,succ i) else (k,i)) ks is
return $ $(testing [|incrementKey c inp|]) (?== out)
ex7 = conjoin [forAll_ $ \(Small i) -> $(testing [|average [fromIntegral i :: Float]|]) (?==fromIntegral i)
,forAllBlind (choose (1,10)) $ \n -> forAll_ $ \(i::Rational) ->
$(testing [|average (replicate n i)|]) (?==i)
,forAll_ $ \(Small i) -> let f = fromIntegral i in $(testing [|average [f-1,f,f+1]|]) (?==f)
,forAll_ $ \(Small i,Small j) ->
let x = fromIntegral i :: Double
y = fromIntegral j :: Double
in $(testing [|average (replicate 10 x ++ replicate 10 y)|]) (?~=(x+y)/2)]
ex8_winner = property $
do names <- shuffle ["Mike","Bob","Lisa","Jane"]
let (win:lose:third:_) = names
loseScore <- choose (0,10000)
diff <- choose (0,10000)
thirdScore <- choose (0,20000)
let inp = Map.fromList [(win,loseScore+diff),(lose,loseScore),(third,thirdScore)]
return $ conjoin [$(testing [|winner inp win lose|]) (?==win)
,$(testing [|winner inp lose win|]) (?==win)]
ex8_tie = property $
do names <- shuffle ["Mike","Bob","Lisa","Jane"]
let (first:second:third:_) = names
score <- choose (0,10000)
thirdScore <- choose (0,20000)
let inp = Map.fromList [(first,score),(second,score),(third,thirdScore)]
return $ $(testing [|winner inp first second|]) (?==first)
ex9_bool = forAll_ $ \bs ->
$(testing [|freqs bs|]) . was $ \m ->
let (t,f) = partition id bs
ntrue = length t
nfalse = length f
out = Map.assocs m
in conjoin [counterexample (" Number of True values should be "++show ntrue) $
null t || (True,ntrue) `elem` out
,counterexample (" Number of False values should be "++show nfalse) $
null f || (False,nfalse) `elem` out]
ex9_int = forAll_ $ \(is::[Int]) ->
$(testing [|freqs is|]) . was $ \out ->
let vals = nub is
in counterexample " Number of keys" (length (Map.keys out) ?== length vals)
.&&.
conjoin (map (ck out is) vals)
where ck out vals i = let exp = length (filter (==i) vals)
in counterexample (" Should contain "++show(i,exp)) $ (i,exp) `elem` (Map.assocs out)
ex10_success = property $
do names <- shuffle ["Mike","Bob","Lisa","Jane"]
let (from:to:third:_) = names
fromBal <- choose (0,100)
toBal <- choose (0,100)
thirdBal <- choose (0,100)
amount <- choose (0,100)
let inp = Map.fromList [(from,fromBal+amount),(to,toBal),(third,thirdBal)]
let out = Map.fromList [(from,fromBal),(to,toBal+amount),(third,thirdBal)]
return $ $(testing [|transfer from to amount inp|]) (?==out)
ex10_missing = property $
do names <- shuffle ["Mike","Bob","Lisa","Jane"]
let (present:missing:third:_) = names
bal <- oneof [return 0, choose (0,100)]
thirdBal <- choose (0,100)
amount <- choose (0,100)
let inp = Map.fromList [(present,bal),(third,thirdBal)]
return $ conjoin [$(testing [|transfer present missing amount inp|]) (?==inp)
,$(testing [|transfer missing present amount inp|]) (?==inp)]
ex10_notEnough = property $
do names <- shuffle ["Mike","Bob","Lisa","Jane"]
let (from:to:third:_) = names
fromBal <- choose (0,100)
toBal <- choose (0,100)
thirdBal <- choose (0,100)
amount <- choose (1,100)
let inp = Map.fromList [(from,fromBal),(to,toBal),(third,thirdBal)]
return $ $(testing [|transfer from to (fromBal+amount) inp|]) (?==inp)
ex11_bool = property $
do small <- choose (0,100::Int)
diff <- choose (1,100)
return $ conjoin [$(testing [|maxIndex (array (False,True) [(False,small),(True,small+diff)])|]) (?==True)
,$(testing [|maxIndex (array (False,True) [(True,small),(False,small+diff)])|]) (?==False)]
ex11_int = property $
do low <- choose (0,100::Int)
hi <- choose (low+1,low+10)
mid <- choose (low,hi)
val <- choose (0,100::Int)
let inp = array (low,hi) [(i,if i==mid then val+1 else val) | i <- [low..hi]]
return $ $(testing [|maxIndex inp|]) (?==mid)
+99
View File
@@ -0,0 +1,99 @@
-- Exercise set 4b: folds
module Set4b where
import Mooc.Todo
------------------------------------------------------------------------------
-- Ex 1: countNothings with a fold. The function countNothings from
-- the course material can be easily implemented using foldr. Your
-- task is to define countHelper so that the following
-- definition of countNothings works.
--
-- Hint: You can start by trying to add a type signature for countHelper.
--
-- Challenge: look up the maybe function and use it in countHelper.
--
-- Examples:
-- countNothings [] ==> 0
-- countNothings [Just 1, Nothing, Just 3, Nothing] ==> 2
countNothings :: [Maybe a] -> Int
countNothings xs = foldr countHelper 0 xs
countHelper = todo
------------------------------------------------------------------------------
-- Ex 2: myMaximum with a fold. Just like in the previous exercise,
-- define maxHelper so that the given definition of myMaximum works.
--
-- Examples:
-- myMaximum [] ==> 0
-- myMaximum [1,3,2] ==> 3
myMaximum :: [Int] -> Int
myMaximum [] = 0
myMaximum (x:xs) = foldr maxHelper x xs
maxHelper = todo
------------------------------------------------------------------------------
-- Ex 3: get the first element of a list with a fold. Define
-- headHelper so that the given definition of myHead works.
--
-- Start by giving headHelper a type.
--
-- Examples:
-- myHead [] ==> Nothing
-- myHead [1,2,3] ==> Just 1
myHead :: [a] -> Maybe a
myHead xs = foldr headHelper Nothing xs
headHelper = todo
------------------------------------------------------------------------------
-- Ex 4: get the last element of a list with a fold. Define lasthelper
-- so that the given definition of myLast works.
--
-- Start by giving lastHelper a type.
--
-- Examples:
-- myLast [] ==> Nothing
-- myLast [1,2,3] ==> Just 3
myLast :: [a] -> Maybe a
myLast xs = foldr lastHelper Nothing xs
lastHelper = todo
------------------------------------------------------------------------------
-- Ex 5: compute the sum and length of a list with a fold. Define
-- slHelper and slStart so that the given definition of sumAndLength
-- works. This could be used to compute the average of a list.
--
-- Start by giving slStart and slHelper types.
--
-- Examples:
-- sumAndLength [1.0,2.0,4.0] ==> (7.0,3)
sumAndLength :: [Double] -> (Double,Int)
sumAndLength xs = foldr slHelper slStart xs
slStart = todo
slHelper = todo
------------------------------------------------------------------------------
-- Ex 6: implement concat with with a fold. Define concatHelper and
-- concatStart so that the given definition of myConcat works.
--
-- Examples:
-- myConcat [[1,2,3],[4,5],[6]] ==> [1,2,3,4,5,6]
myConcat :: [[a]] -> [a]
myConcat xs = foldr concatHelper concatStart xs
concatStart = todo
concatHelper = todo
+65
View File
@@ -0,0 +1,65 @@
{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-}
module Set4bTest where
import Data.Char
import Data.List
import Test.QuickCheck
import Mooc.Th
import Mooc.Test
import Set4b
main = score tests
tests = [(1,"countNothings",[ex1_countNothings])
,(2,"myMaximum",[ex2_myMaximum])
,(3,"myHead",[ex3_myHead_empty, ex3_myHead_full])
,(4,"myLast",[ex4_myLast_empty, ex4_myLast_full])
,(5,"sumAndLength",[ex5_sumAndLength])
,(6,"myConcat",[ex6_myConcat])]
ex1_countNothings = property $ do
justs <- fmap (map Just) $ listOf (choose (0::Int,10))
nothings <- listOf (return Nothing)
input <- shuffle (justs++nothings)
return $
counterexample ("input = "++show input) $
$(testing' [|foldr countHelper 0 input|]) (?==length nothings)
ex2_myMaximum = property $ do
x <- choose (-100::Int,100)
xs <- listOf (choose (-100,100::Int))
return $
counterexample ("x = "++show x) $
counterexample ("xs = "++show xs) $
$(testing' [|foldr maxHelper x xs|]) (?==maximum (x:xs))
ex3_myHead_empty =
$(testing' [|foldr headHelper Nothing []|]) (?==(Nothing::Maybe Bool))
ex3_myHead_full =
forAll_ $ \(NonEmpty xs::NonEmptyList Int) ->
counterexample ("xs = "++show xs) $
$(testing' [|foldr headHelper Nothing xs|]) (?==Just (head xs))
ex4_myLast_empty =
$(testing' [|foldr lastHelper Nothing []|]) (?==(Nothing::Maybe Bool))
ex4_myLast_full =
forAll_ $ \(NonEmpty xs::NonEmptyList Int) ->
counterexample ("xs = "++show xs) $
$(testing' [|foldr lastHelper Nothing xs|]) (?==Just (last xs))
niceDouble = fmap ((/2).fromIntegral) (arbitrary :: Gen Int)
ex5_sumAndLength =
forAllBlind (listOf niceDouble) $ \(ds::[Double]) ->
counterexample ("ds = "++show ds) $
$(testing' [|foldr slHelper slStart ds|]) (?==(sum ds, length ds))
ex6_myConcat =
forAll_ $ \(xs :: [[Int]]) ->
counterexample ("xs = "++show xs) $
$(testing' [|foldr concatHelper concatStart xs|]) (?==concat xs)