diff --git a/exercises/Set2.hs b/exercises/Set2.hs new file mode 100644 index 0000000..01849e0 --- /dev/null +++ b/exercises/Set2.hs @@ -0,0 +1,141 @@ +-- Exercise set 2: +-- * Guards +-- * Lists +-- * Maybe +-- * Either +-- +-- Functions you will need: +-- * head, tail +-- * take, drop +-- * length +-- * null + + +module Set2 where + +import Mooc.Todo + +-- Some imports you'll need. Don't add other imports :) +import Data.List + +------------------------------------------------------------------------------ +-- Ex 1: Define the constant years, that is a list of the values 1982, +-- 2004 and 2020 in this order. + +years = todo + +------------------------------------------------------------------------------ +-- Ex 2: define the function takeFinal, which returns the n last +-- elements of the given list. +-- +-- If the list is shorter than n, return all elements. +-- +-- Hint! remember the take and drop functions. + +takeFinal :: Int -> [a] -> [a] +takeFinal n xs = todo + +------------------------------------------------------------------------------ +-- Ex 3: Update an element at a certain index in a list. More +-- precisely, return a list that is identical to the given list except +-- the element at index i is x. +-- +-- Note! indexing starts from 0 +-- +-- Examples: +-- updateAt 0 4 [1,2,3] ==> [4,2,3] +-- updateAt 2 0 [4,5,6,7] ==> [4,5,0,7] + +updateAt :: Int -> a -> [a] -> [a] +updateAt i x xs = todo + +------------------------------------------------------------------------------ +-- Ex 4: substring i j s should return the substring of s starting at +-- index i and ending at index j. +-- +-- Remember that strings are lists! + +substring :: Int -> Int -> String -> String +substring i j s = todo + +------------------------------------------------------------------------------ +-- Ex 5: implement the function palindromify that chops a character +-- off the front _and_ back of a string until the result is a +-- palindrome. +-- +-- Examples: +-- palindromify "ab" ==> "" +-- palindromify "aaay" ==> "aa" +-- palindromify "xabbay" ==> "abba" +-- palindromify "abracacabra" ==> "acaca" + +palindromify :: String -> String +palindromify s = todo + +------------------------------------------------------------------------------ +-- Ex 6: implement safe integer devision, that is, a function that +-- returns a Just result normally, but Nothing if the divisor is zero. +-- +-- Remember that integer division can be done with the div function. +-- +-- Examples: +-- safeDiv 4 2 ==> Just 2 +-- safeDiv 4 0 ==> Nothing + +safeDiv :: Integer -> Integer -> Maybe Integer +safeDiv x y = todo + +------------------------------------------------------------------------------ +-- Ex 7: implement a function greet that greets a person given a first +-- name and possibly a last name. The last name is represented as a +-- Maybe String value. +-- +-- Examples: +-- greet "John" Nothing ==> "Hello, John!" +-- greet "John" (Just "Smith") ==> "Hello, John Smith!" + +greet :: String -> Maybe String -> String +greet first last = todo + +------------------------------------------------------------------------------ +-- Ex 8: safe list indexing. Define a function safeIndex so that +-- safeIndex xs i +-- gets the element at index i in the list xs. If i is not a valid +-- index, Nothing is returned. +-- +-- Examples: +-- safeIndex [True] 1 ==> Nothing +-- safeIndex [10,20,30] 0 ==> Just 10 +-- safeIndex [10,20,30] 2 ==> Just 30 +-- safeIndex [10,20,30] 3 ==> Nothing +-- safeIndex ["a","b","c"] (-1) ==> Nothing + +safeIndex :: [a] -> Int -> Maybe a +safeIndex xs i = todo + +------------------------------------------------------------------------------ +-- Ex 9: another variant of safe division. This time you should use +-- Either to return a string error message. +-- +-- Examples: +-- eitherDiv 4 2 ==> Right 2 +-- eitherDiv 4 0 ==> Left "4/0" + +eitherDiv :: Integer -> Integer -> Either String Integer +eitherDiv x y = todo + +------------------------------------------------------------------------------ +-- Ex 10: implement the function addEithers, which combines two values of type +-- Either String Int into one like this: +-- +-- - If both inputs were Ints, sum the Ints +-- - Otherwise, return the first argument that was not an Int +-- +-- Exmaples: +-- +-- addEithers (Right 1) (Right 2) ==> Right 3 +-- addEithers (Right 1) (Left "fail") ==> Left "fail" +-- addEithers (Left "boom") (Left "fail") ==> Left "boom" + +addEithers :: Either String Int -> Either String Int -> Either String Int +addEithers a b = todo diff --git a/exercises/Set2Test.hs b/exercises/Set2Test.hs new file mode 100644 index 0000000..b4dfc3c --- /dev/null +++ b/exercises/Set2Test.hs @@ -0,0 +1,115 @@ +{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} + +module Set2Test where + +import Data.Char +import Data.List +import Test.QuickCheck + +import Mooc.Th +import Mooc.Test + +import Set2 + +main = score tests + +tests = [(1,"years",[ex1_years]) + ,(2,"takeFinal",[ex2_takeFinal_1, ex2_takeFinal_2]) + ,(3,"updateAt",[ex3_updateAt]) + ,(4,"substring",[ex4_substring]) + ,(5,"palindromify",[ex5_palindromify]) + ,(6,"safeDiv",[ex6_safeDiv_zero, ex6_safeDiv_nonZero]) + ,(7,"greet",[ex7_greet_nothing, ex7_greet_just]) + ,(8,"safeIndex",[ex8_safeIndex_ok, ex8_safeIndex_fail]) + ,(9,"eitherDiv",[ex9_eitherDiv_zero, ex9_eitherDiv_nonZero]) + ,(10,"addEithers",[ex10_addEithers_both, ex10_addEithers_one, ex10_addEithers_neither]) + ] + +-- -- -- -- -- -- + +ex1_years = + let origLength = length in + let length = origLength :: [a] -> Int in + conjoin [$(testing' [|length years|]) (?== 3) + ,$(testing' [|head years|]) (?== 1982) + ,$(testing' [|years !! 1|]) (?== 2004) + ,$(testing' [|last years|]) (?== 2020)] + +letter = choose ('a','z') +word = listOf1 letter + +ex2_takeFinal_1 = property $ do + n <- choose (0,20) + k <- choose (0,n) + return $ $(testing [|takeFinal k [0..n]|]) (?== [n-k+1..n]) + +ex2_takeFinal_2 = property $ do + n <- choose (0,20) + k <- choose (0,n) + let inp = reverse [0..n] + return $ $(testing [|takeFinal k inp|]) (?== reverse [0..k-1]) + +ex3_updateAt = property $ do + ws <- listOf1 word + i <- choose (0,length ws - 1) + j <- choose (0,length ws - 1) + w <- word + return . $(testing [|updateAt i w ws|]) $ \v -> + conjoin [counterexample ("index "++show i) $ v!!i ?== w + ,counterexample ("index "++show j) $ i/=j ==> v!!j ?== ws!!j] + +ex4_substring = forAll_ $ \(prefix :: String, target, suffix) -> + $(testing [|substring (length prefix) (length prefix + length target) (prefix ++ target ++ suffix)|]) (?==target) + +ex5_palindromify = property $ do + beef <- word + center <- sublistOf "z" + bun <- fmap nub word `suchThat` \w -> length w > 1 + let answer = beef ++ center ++ reverse beef + input = bun ++ answer ++ bun + return $ $(testing [|palindromify input|]) (?== answer) + +ex6_safeDiv_zero = forAll_ $ \x -> + $(testing [|safeDiv x 0|]) (?== Nothing) + +ex6_safeDiv_nonZero = forAll_ $ \(x,y) -> + (y/=0) ==> $(testing [|safeDiv x y|]) (?== Just (div x y)) + +ex7_greet_nothing = forAllBlind word $ \first -> + $(testing [|greet first (Nothing :: Maybe String)|]) (?==("Hello, "++first++"!")) + +ex7_greet_just = forAllBlind word $ \first -> + forAllBlind word $ \last -> + $(testing [|greet first (Just last)|]) (?==("Hello, "++first++" "++last++"!")) + +ex8_safeIndex_ok = property $ do + ws <- listOf1 word + i <- choose (0,length ws - 1) + return $ $(testing [|safeIndex ws i|]) (?==Just (ws!!i)) + +ex8_safeIndex_fail = property $ do + is <- listOf1 (choose (10,20::Int)) + i <- choose (0,10) + let neg = negate i - 1 + let over = length is + i + return $ conjoin [$(testing [|safeIndex is neg|]) (?==Nothing) + ,$(testing [|safeIndex is over|]) (?==Nothing)] + +ex9_eitherDiv_zero = forAll_ $ \x -> + $(testing [|eitherDiv x 0|]) (?== Left (show x++"/0")) + +ex9_eitherDiv_nonZero = forAll_ $ \(x,y) -> + (y/=0) ==> $(testing [|eitherDiv x y|]) (?== Right (div x y)) + + +ex10_addEithers_both = forAll_ $ \(x,y) -> + $(testing [|addEithers (Right x :: Either String Int) (Right y :: Either String Int)|]) (?== Right (x+y)) + +ex10_addEithers_one = property $ do + err <- fmap Left arbitrary + int <- fmap Right arbitrary + ~[x,y] <- shuffle [err,int] + return $ $(testing [|addEithers x y|]) (?==err) + +ex10_addEithers_neither = forAll_ $ \(a,b) -> + $(testing [|addEithers (Left a :: Either String Int) (Left b :: Either String Int)|]) (?==Left a) diff --git a/exercises/Set3a.hs b/exercises/Set3a.hs new file mode 100644 index 0000000..5e4f9d3 --- /dev/null +++ b/exercises/Set3a.hs @@ -0,0 +1,229 @@ +-- Exercise set 3a +-- +-- * lists +-- * functional programming + + +module Set3a where + +import Mooc.Todo + +-- Some imports you'll need. +-- Do not add any other imports! :) +import Data.List +import Data.Char + +------------------------------------------------------------------------------ +-- Ex 1: implement the function maxBy that takes as argument a +-- measuring function (of type a -> Int) and two values (of type a). +-- +-- maxBy should apply the measuring function to both arguments and +-- return the argument for which the measuring function returns a +-- higher value. +-- +-- Examples: +-- +-- maxBy (*2) 3 5 ==> 5 +-- maxBy length [1,2,3] [4,5] ==> [1,2,3] +-- maxBy head [1,2,3] [4,5] ==> [4,5] + +maxBy :: (a -> Int) -> a -> a -> a +maxBy measure a b = todo + +------------------------------------------------------------------------------ +-- Ex 2: implement the function mapMaybe that takes a function and a +-- Maybe value. If the value is Nothing, it returns Nothing. If it is +-- a Just, it updates the contained value using the function. +-- +-- Examples: +-- mapMaybe length Nothing ==> Nothing +-- mapMaybe length (Just "abc") ==> Just 3 + +mapMaybe :: (a -> b) -> Maybe a -> Maybe b +mapMaybe f x = todo + +------------------------------------------------------------------------------ +-- Ex 3: implement the function mapMaybe2 that works like mapMaybe +-- except it combines two Maybe values using a function of two +-- arguments. +-- +-- Examples: +-- mapMaybe2 take (Just 2) (Just "abcd") ==> Just "ab" +-- mapMaybe2 div (Just 6) (Just 3) ==> Just 2 +-- mapMaybe2 div Nothing (Just 3) ==> Nothing +-- mapMaybe2 div (Just 6) Nothing ==> Nothing + +mapMaybe2 :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c +mapMaybe2 f x y = todo + +------------------------------------------------------------------------------ +-- Ex 4: define the functions firstHalf and palindrome so that +-- palindromeHalfs returns the first halfs of all palindromes in its +-- input. +-- +-- Example: +-- palindromeHalfs ["abba", "cat", "racecar"] +-- ==> ["ab","race"] +-- +-- What types should firstHalf and palindrome have? Give them type +-- annotations. +-- +-- Note! Do not change the definition of palindromeHalfs + +palindromeHalfs :: [String] -> [String] +palindromeHalfs xs = map firstHalf (filter palindrome xs) + +firstHalf = todo + +palindrome = todo + +------------------------------------------------------------------------------ +-- Ex 5: Implement a function capitalize that takes in a string and +-- capitalizes the first letter of each word in it. +-- +-- You should probably define a helper function capitalizeFirst that +-- capitalizes the first letter of a string. +-- +-- These functions will help: +-- - toUpper :: Char -> Char from the module Data.Char +-- - words :: String -> [String] +-- - unwords :: [String] -> String +-- +-- Example: +-- capitalize "goodbye cruel world" ==> "Goodbye Cruel World" + +capitalize :: String -> String +capitalize = todo + +------------------------------------------------------------------------------ +-- Ex 6: powers k max should return all the powers of k that are less +-- than or equal to max. For example: +-- +-- powers 2 5 ==> [1,2,4] +-- powers 3 30 ==> [1,3,9,27] +-- powers 2 2 ==> [1,2] +-- +-- You can assume that k is at least 2. +-- +-- Hints: +-- * k^max > max +-- * the function takeWhile + +powers :: Int -> Int -> [Int] +powers n max = todo + +------------------------------------------------------------------------------ +-- Ex 7: implement a functional while loop. That is, a function while +-- that that takes an updating function, a checking function and an +-- initial value. While should repeatedly apply the updating function +-- to the initial value as long as the value passes the checking function. +-- Finally, the value that doesn't pass the check is returned. +-- +-- Examples: +-- +-- while odd (+1) 1 ==> 2 +-- +-- while (<=4) (+1) 0 ==> 5 +-- +-- let check [] = True +-- check ('A':xs) = False +-- check _ = True +-- in while check tail "xyzAvvt" +-- ==> Avvt + +while :: (a->Bool) -> (a->a) -> a -> a +while check update initial = todo + +------------------------------------------------------------------------------ +-- Ex 8: another version of a while loop. This time, the check +-- function returns an Either value. Keep iterating on Right values, +-- stop on a Left value. +-- +-- Examples (see definition of step below): +-- whileEither (step 100) 1 ==> 128 +-- whileEither (step 1000) 3 ==> 1536 + +whileRight :: (a -> Either b a) -> a -> b +whileRight f x = todo + +-- for the whileRight examples: +-- step k x doubles x if it's less than k +step :: Int -> Int -> Either Int Int +step k x = if x ["aa","ab","ba","bb"] +-- joinToLength 5 ["a","b","cd","def"] ==> ["cddef","defcd"] +-- +-- Hint! This is a great use for list comprehensions + +joinToLength :: Int -> [String] -> [String] +joinToLength = todo + +------------------------------------------------------------------------------ +-- Ex 10: implement the operator +|+ that returns a list with the first +-- elements of its input lists. +-- +-- Give +|+ a type signature. +-- +-- Examples: +-- [1,2,3] +|+ [4,5,6] ==> [1,4] +-- [] +|+ [True] ==> [True] +-- [] +|+ [] ==> [] + + +------------------------------------------------------------------------------ +-- Ex 11: remember the lectureParticipants example from Lecture 2? We +-- used a value of type [Either String Int] to store some measurements +-- that might be missing. Implement the function sumRights which sums +-- all non-missing measurements in a list like this. +-- +-- Challenge: look up the type of the either function. Implement +-- sumRights using the map & either functions instead of pattern +-- matching on lists or Eithers! +-- +-- Examples: +-- sumRights [Right 1, Left "bad value", Right 2] ==> 3 +-- sumRights [Left "bad!", Left "missing"] ==> 0 + +sumRights :: [Either a Int] -> Int +sumRights = todo + +------------------------------------------------------------------------------ +-- Ex 12: in this exercise you get to implement an interpreter for a +-- simple language. You should keep track of the x and y coordinates, +-- and interpret the following commands: +-- +-- up -- increment y by one +-- down -- decrement y by one +-- left -- decrement x by one +-- right -- increment x by one +-- printX -- print value of x +-- printY -- print value of y +-- +-- The interpreter will be a function of type [String] -> [String]. +-- Its input is a list of commands, and its output is a list of the +-- results of the print commands in the input. +-- +-- Both coordinates start at 0. +-- +-- Examples: +-- +-- interpreter ["up","up","up","printY","down","printY"] ==> ["3","2"] +-- interpreter ["up","right","right","printY","printX"] ==> ["1","2"] +-- +-- Surprise! after you've implemented the function, try running this in GHCi: +-- interact (unlines . interpreter . lines) +-- after this you can enter commands on separate lines and see the +-- responses to them +-- +-- Unfortunately the surprise might not work if you've implemented +-- your interpreter correctly but weirdly :( + +interpreter :: [String] -> [String] +interpreter commands = todo diff --git a/exercises/Set3aTest.hs b/exercises/Set3aTest.hs new file mode 100644 index 0000000..d350557 --- /dev/null +++ b/exercises/Set3aTest.hs @@ -0,0 +1,187 @@ +{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} + +module Set3aTest where + +import Data.Char +import Data.List +import Test.QuickCheck + +import Mooc.Th +import Mooc.Test + +import Set3a + +main = score tests + +tests = [(1,"maxBy",[ex1_maxBy]) + ,(2,"mapMaybe",[ex2_mapMaybe_Nothing, ex2_mapMaybe_Just_String, ex2_mapMaybe_Just_Int]) + ,(3,"mapMaybe2",[ex3_mapMaybe2]) + ,(4,"palindromeHalfs",[ex4_palindromeHalfs]) + ,(5,"capitalize",[ex5_capitalize_1, ex5_capitalize_2]) + ,(6,"powers",[ex6_powers]) + ,(7,"while",[ex7_while_number, ex7_while_string]) + ,(8,"whileRight",[ex8_whileRight_Left, ex8_whileRight_step]) + ,(9,"joinToLength",[ex9_1]) + ,(10,"+|+",[ex10]) + ,(11,"sumRights",[ex11]) + ,(12,"interpreter",[ex12_interpreter_1, ex12_interpreter_2]) + ] + +-- -- -- -- -- -- + +letter = choose ('a','z') +word = listOf1 letter + +ex1_maxBy = property $ do + t <- choose (0,20) + f <- choose (0,20) `suchThat` \f -> f/=t + let p True = t + p False = f + return $ + counterexample ("let p True = "++show t++"; p False = "++show f++" in maxBy p False True") $ + maxBy p False True ?== (t>f) + +ex2_mapMaybe_Nothing = + $(testing' [|mapMaybe negate Nothing|]) (?==Nothing) + +ex2_mapMaybe_Just_String = forAllBlind word $ \w -> + counterexample ("mapMaybe length (Just " ++ show w ++ ")") $ + mapMaybe length (Just w) ?== Just (length w) + +ex2_mapMaybe_Just_Int = forAll_ $ \(i::Int) -> + counterexample ("mapMaybe negate (Just " ++ show i ++ ")") $ + mapMaybe negate (Just i) ?== Just (negate i) + +ex3_mapMaybe2 = forAll_ $ \(Positive i,Positive j) -> + let t :: Maybe Integer -> Maybe Integer -> Maybe Integer -> Property + t a b exp = counterexample ("mapMaybe div " ++ show' a ++ " " ++ show' b) $ mapMaybe2 div a b ?== exp + in conjoin [t (Just i) (Just j) (Just (div i j)) + ,t Nothing (Just j) Nothing + ,t (Just i) Nothing Nothing + ,t Nothing Nothing Nothing] + +unpalindrome = word `suchThat` \w -> w /= reverse w + +ex4_palindromeHalfs = property $ do + half1 <- word + let palindrome1 = half1 ++ reverse half1 + half2 <- word + let palindrome2 = half2 ++ reverse (init half2) + un1 <- listOf1 unpalindrome + un2 <- listOf1 unpalindrome + un3 <- listOf1 unpalindrome + let input = un1 ++ [palindrome1] ++ un2 ++ [palindrome2] ++ un3 + let expected = [half1, half2] + let palindromeHalfs xs = map firstHalf (filter palindrome xs) + return $ $(testing [|palindromeHalfs input|]) (?==expected) + +ex5_capitalize_1 = property $ do + cap <- letter + w1 <- word + w2 <- word + return $ $(testing [|capitalize (cap:w1++" "++cap:w2)|]) (?==(toUpper cap:w1++" "++toUpper cap:w2)) + +ex5_capitalize_2 = property $ do + ws <- listOf1 word + let input = unwords ws + let positions = init $ scanl (+) 0 $ map (succ.length) ws + p <- elements positions + q <- elements [0..length input - 1] + return . $(testing [|capitalize input|]) $ \v -> + conjoin [counterexample ("char at index "++show p) $ v !! p ?== toUpper (input !! p) + ,not (elem q positions) ==> counterexample ("char at index "++show q) (v !! q ?== input !! q)] + +ex6_powers = property $ do + n <- choose (2,5) + len <- choose (1,10) + end <- choose (n^(len-1),n^len-1) + return $ $(testing [|powers n end|]) $ \p -> conjoin + [counterexample "all smaller than end" $ all (<=end) p + ,counterexample "in ascending order" $ p == sort p + ,counterexample "length" $ length p ?== len + ,counterexample "all powers of n" $ all (check n) p] + where check n 0 = True + check n 1 = True + check n k + | k `mod` n == 0 = check n (div k n) + | otherwise = False + +ex7_while_number = property $ do + n <- choose (0,20 :: Integer) + return $ counterexample ("while (/="++show n++") (+1) 0") $ + while (/=n) (+1) 0 ?== n + +ex7_while_string = property $ do + n <- word + let w = n++n + p = (/=n) + return $ counterexample ("while (/="++show n++") tail "++show w) $ + while p tail w == n + +ex8_whileRight_Left = forAllBlind word $ \w -> + forAllBlind (choose (1::Int,10)) $ \i -> + counterexample ("let f _ = Left " ++ show w ++ " in whileRight f " ++ show i) $ + let f _ = Left w in whileRight f i ?== w + +ex8_whileRight_step = property $ do + let step :: Int -> Int -> Either Int Int + step k x = if x length x + length y == target) $ + filter (\x -> 2 * length x /= target) $ + filter (\x -> diffLength x v && diffLength x w) $ + cruft + output = [w++v, v++w] + return $ $(testing [|joinToLength target input|]) (hasElements output) + where diffLength a b = length a /= length b + +ex10 = $(withDefined "+|+") $ \op -> + forAll_ $ \(x::Int,y::Int,xs'::[Int],ys'::[Int]) -> + let xs = x:xs' + ys = y:ys' + in conjoin [counterexample (show xs ++ " +|+ " ++ show ys) $ op xs ys ?== [x,y] + ,counterexample (show xs ++ " +|+ []") $ op xs [] ?== [x] + ,counterexample ("[] +|+ " ++ show xs) $ op [] xs ?== [x]] + +ex11 = property $ do + is <- listOf (choose (0,10)) + es <- listOf word + input <- shuffle (map Right is ++ map Left es) + return $ $(testing [|sumRights input|]) (?==sum is) + +ex12_interpreter_1 = property $ do + up <- choose (0,10) + right <- choose (0,10) + down <- choose (1,3) + left <- choose (0,1) + + let first = replicate up "up" ++ replicate right "right" ++ ["printY","printX"] + second = replicate down "down" ++ replicate left "left" ++ ["printY","printX"] + input = first ++ second + output = [show up, show right, show (up-down), show (right-left)] + + return $ counterexample ("interpreter "++show input) $ + interpreter input ?== output + +ex12_interpreter_2 = property $ do + nums <- vectorOf 4 $ choose (0,10) + let diffs = zipWith (-) nums (0:nums) + f x | x<0 = replicate (negate x) "down" + | otherwise = replicate x "up" + input = concatMap (\x -> f x ++ ["printY"]) diffs + output = map show nums + return $ $(testing [|interpreter input|]) (?== output) diff --git a/exercises/Set3b.hs b/exercises/Set3b.hs new file mode 100644 index 0000000..8bc1f68 --- /dev/null +++ b/exercises/Set3b.hs @@ -0,0 +1,163 @@ +-- Exercise set 3b +-- +-- This is a special exercise set. The exercises are about +-- implementing list functions using recursion and pattern matching, +-- without using any standard library functions. For this reason, +-- you'll be working in a limited environment where almost none of the +-- standard library is available. +-- +-- The tests will check that you haven't added imports :) + +{-# LANGUAGE NoImplicitPrelude #-} + +module Set3b where + +import Mooc.LimitedPrelude +import Mooc.Todo + + +------------------------------------------------------------------------------ +-- Ex 1: given numbers start, count and end, build a list that starts +-- with with count copies of start and ends with end. +-- +-- Use recursion and the : operator to build the list. +-- +-- Examples: +-- buildList 1 5 2 ==> [1,1,1,1,1,2] +-- buildList 7 0 3 ==> [3] + +buildList :: Int -> Int -> Int -> [Int] +buildList start count end = todo + +------------------------------------------------------------------------------ +-- Ex 2: given i, build the list of sums [1, 1+2, 1+2+3, .., 1+2+..+i] +-- +-- Use recursion and the : operator to build the list. +-- +-- Ps. you'll probably need a recursive helper function + +sums :: Int -> [Int] +sums i = todo + +------------------------------------------------------------------------------ +-- Ex 3: define a function mylast that returns the last value of the +-- given list. For an empty list, a provided default value is +-- returned. +-- +-- Use only pattern matching and recursion (and the list constructors : and []) +-- +-- Examples: +-- mylast 0 [] ==> 0 +-- mylast 0 [1,2,3] ==> 3 + +mylast :: a -> [a] -> a +mylast def xs = todo + +------------------------------------------------------------------------------ +-- Ex 4: safe list indexing. Define a function indexDefault so that +-- indexDefault xs def i +-- gets the element at index i in the list xs. If i is not a valid +-- index, def is returned. +-- +-- Use only pattern matching and recursion (and the list constructors : and []) +-- +-- This time, implement indexDefault using pattern matching and +-- recursion. +-- +-- Examples: +-- indexDefault [True] 1 False ==> False +-- indexDefault [10,20,30] 0 7 ==> 10 +-- indexDefault [10,20,30] 2 7 ==> 30 +-- indexDefault [10,20,30] 3 7 ==> 7 +-- indexDefault ["a","b","c"] (-1) "d" ==> "d" + +indexDefault :: [a] -> Int -> a -> a +indexDefault xs i def = todo + +------------------------------------------------------------------------------ +-- Ex 5: define a function that checks if the given list is in +-- increasing order. +-- +-- Use only pattern matching and recursion. + +sorted :: [Int] -> Bool +sorted xs = todo + +------------------------------------------------------------------------------ +-- Ex 6: compute the partial sums of the given list like this: +-- +-- sumsOf [a,b,c] ==> [a,a+b,a+b+c] +-- sumsOf [a,b] ==> [a,a+b] +-- sumsOf [] ==> [] +-- +-- Use pattern matching and recursion (and the list constructors : and []) + +sumsOf :: [Int] -> [Int] +sumsOf xs = todo + +------------------------------------------------------------------------------ +-- Ex 7: implement the function merge that merges two sorted lists of +-- Ints into a sorted list +-- +-- Use only pattern matching and recursion (and the list constructors : and []) +-- +-- Examples: +-- merge [1,3,5] [2,4,6] ==> [1,2,3,4,5,6] +-- merge [1,1,6] [1,2] ==> [1,1,1,2,6] + +merge :: [Int] -> [Int] -> [Int] +merge xs ys = todo + +------------------------------------------------------------------------------ +-- Ex 8: define the function mymaximum that takes a list and a +-- function bigger :: a -> a -> Bool and returns the +-- biggest of the list, according to the comparing function. +-- +-- An initial biggest value is provided to give you something to +-- return for empty lists. +-- +-- Examples: +-- mymaximum (>) 3 [] ==> 3 +-- mymaximum (>) 0 [1,3,2] ==> 3 +-- mymaximum (>) 4 [1,3,2] ==> 4 -- initial value was biggest +-- mymaximum (<) 4 [1,3,2] ==> 1 -- note changed biggerThan +-- mymaximum (\_ _ -> True) 4 [1,3,2] +-- ==> 2 -- new value is always the biggest, so returns the last + +mymaximum :: (a -> a -> Bool) -> a -> [a] -> a +mymaximum bigger initial xs = todo + +------------------------------------------------------------------------------ +-- Ex 9: define a version of map that takes a two-argument function +-- and two lists. Example: +-- map2 f [x,y,z,w] [a,b,c] ==> [f x a, f y b, f z c] +-- +-- Use recursion and pattern matching. Do not use any library functions. + +map2 :: (a -> b -> c) -> [a] -> [b] -> [c] +map2 f as bs = todo + + +------------------------------------------------------------------------------ +-- Ex 10: implement the function maybeMap, which works a bit like a +-- combined map & filter. +--- +-- maybeMap is given a list ([a]) and a function of type a -> Maybe b. +-- This function is called for all values in the list. If the function +-- returns Just x, x will be in the result list. If the function +-- returns Nothing, nothing gets added to the result list. +-- +-- Examples: +-- +-- let f x = if x>0 then Just (2*x) else Nothing +-- in maybeMap f [0,1,-1,4,-2,2] +-- ==> [2,8,4] +-- +-- maybeMap Just [1,2,3] +-- ==> [1,2,3] +-- +-- maybeMap (\x -> Nothing) [1,2,3] +-- ==> [] + +maybeMap :: (a -> Maybe b) -> [a] -> [b] +maybeMap f xs = todo diff --git a/exercises/Set3bTest.hs b/exercises/Set3bTest.hs new file mode 100644 index 0000000..f510497 --- /dev/null +++ b/exercises/Set3bTest.hs @@ -0,0 +1,109 @@ +{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-} + +module Set3bTest where + +import Data.List +import Test.QuickCheck + +import Mooc.Th +import Mooc.Test + +import Set3b + +main = score tests + +tests = [(1,"buildList",[imports, ex1_buildList]) + ,(2,"sums",[imports, ex2_sums]) + ,(3,"mylast",[imports, ex3_mylast_nonempty, ex3_mylast_empty]) + ,(4,"indexDefault",[imports, ex4_indexDefault_ok, ex4_indexDefault_ok]) + ,(5,"sorted",[imports, ex5_sorted_empty, ex5_sorted]) + ,(6,"sumsOf",[imports, ex6_sumsOf]) + ,(7,"merge",[imports, ex7_merge]) + ,(8,"mymaximum",[imports, ex8_mymaximum_max, ex8_mymaximum_min, ex8_mymaximum_empty]) + ,(9,"map2",[imports, ex9_map2_1, ex9_map2_2]) + ,(10,"maybeMap",[imports, ex10_maybeMap_1, ex10_maybeMap_2]) + ] + +-- -- -- -- -- -- + +imports = $(importsOnly "Set3b" ["GHC.Num", "GHC.Base", "GHC.Classes", "GHC.Types", + "Mooc.Todo", "Mooc.LimitedPrelude"]) + +ex1_buildList = property $ do + start <- choose (0,20) + count <- choose (0,10) + end <- choose (30,40) + return $ $(testing [|buildList start count end|]) (?==(replicate count start ++ [end])) + +ex2_sums = property $ do + i <- choose (1,30) + return $ $(testing [|sums i|]) (?==scanl1 (+) [1..i]) + + +ex3_mylast_nonempty = forAll_ $ \(NonEmpty xs::NonEmptyList Integer) -> + $(testing [|mylast 0 xs|]) (?== last xs) + +ex3_mylast_empty = forAll_ $ \(b::Bool) -> + $(testing [|mylast b ([]::[Bool])|]) (?== b) + +ex4_indexDefault_ok = property $ do + ws <- listOf1 arbitrary :: Gen [Int] + i <- choose (0,length ws - 1) + def <- arbitrary + return $ $(testing [|indexDefault ws i def|]) (?==ws!!i) + +ex4_indexDefault_fail = property $ do + is <- listOf1 arbitrary :: Gen [Int] + i <- choose (0,10) + def <- arbitrary + let neg = negate i - 1 + let over = length is + i + return $ conjoin [$(testing [|indexDefault is neg def|]) (?==def) + ,$(testing [|indexDefault is over def|]) (?==def)] + +ex5_sorted_empty = + $(testing [|sorted ([]::[Int])|]) (?== True) + +ex5_sorted = forAll_ $ \xs -> + let s = sort xs + in conjoin [$(testing [|sorted xs|]) (?== (s == xs)) + ,$(testing [|sorted s|]) (?== True)] + + +ex6_sumsOf = forAll_ $ \xs -> $(testing [|sumsOf xs|]) (?== scanl1 (+) xs) + +ex7_merge = forAll_ $ \(xs,ys) -> + $(testing [|merge (sort xs) (sort ys)|]) (?== sort (xs ++ ys)) + +ex8_mymaximum_max = forAll_ $ \(NonEmpty xs :: NonEmptyList Int) -> + counterexample ("mymaximum (>) 0 " ++ show xs) $ + mymaximum (>) 0 xs ?== max 0 (maximum xs) + +ex8_mymaximum_min = forAll_ $ \(NonEmpty xs :: NonEmptyList Int) -> + counterexample ("mymaximum (<) 10000 " ++ show xs) $ + mymaximum (<) 10000 xs ?== min 10000 (minimum xs) + +ex8_mymaximum_empty = property $ do + i <- choose (0,10) :: Gen Int + return $ counterexample ("mymaximum (<) "++show i++" []") $ mymaximum (<) i [] ?== i + +ex9_map2_1 = forAll_ $ \(i::[Int],j::[Bool]) -> + counterexample ("map2 const "++show i++" "++show j) $ + map2 const i j ?== take (length j) i + +ex9_map2_2 = forAll_ $ \(is::[Int],j::Int) -> + let js = replicate (length is) j + in counterexample ("map2 (+) "++show is++" "++show js) $ + map2 (+) is js ?== map (+j) is + +ex10_maybeMap_1 = forAll_ $ \bs -> + counterexample ("let f True = Just True; f False = Nothing in maybeMap f "++show bs) $ + maybeMap f bs ?== filter id bs + where f True = Just True + f False = Nothing + +ex10_maybeMap_2 = forAll_ $ \(is :: [Int]) -> + counterexample ("let f x = if x>0 then Just (2*x) else Nothing\ + \in maybeMap f "++show is) $ + maybeMap f is ?== map (2*) (filter (>0) is) + where f x = if x>0 then Just (2*x) else Nothing