Homework 1
Instructions
The source code of this homework can be found here. You should fill in the definitions of the required functions but do not change the types of the functions.
How to submit: Send an email to niki.vazou@imdea.org with subject Haskell-Course'19:HW1 and attach this file and a MinMax.lhs file.
> module HW1 whereProblem 1: Factoring
We say that a is a factor of n when there exists an integer b so that n = a * b.
- Define a function
factors nthat returns all functors ofn. For example,factors 12 = [1,2,3,4,6,12]andfactors 13 = [1,13].
> factors :: Int -> [Int]
> factors n = error "Define me!"We say that an integer n is prime when its only factors are 1 and n.
- Define a function
isPrime nthat returnsTrueif and only ifnis prime. For exampleisPrime 12 = FalseandisPrime 13 = True.
> isPrime :: Int -> Bool
> isPrime n = error "Define me!"- Define the function
primesthat returns the list of all prime ints.
> primes :: [Int]
> primes = error "Define me!"Hint: You can define the above function in 20 characters. If you want a challenge, you can use 5 more characters and define primes by only using library functions (from Prelude and Data.List).
- Optimize the
factors nfunction to only call themodfunction at most √ n times. Note that factors appear in pairs. If you foundito be a factor ofn, thendiv n iis also a factor ofn. Hint: define a helper recursive functionfactorsRec n iand recurse on increasingi.
> factorsOpt :: Int -> [Int]
> factorsOpt n = error "Define me!"Test your optimization. The below function
sameElems xs yschecks that the two input lists have the same elements, by checking that- the two input lists have same length and
- all elements of the list
xsare elements of the listys.
> sameElems :: [Int] -> [Int] -> Bool
> sameElems xs ys = length xs == length ys
> && all (`elem` ys) xsUse sameElems to write a function testFactors n that tests that the two factor functions you wrote above return the same factors for every integer up to n. Hint: use the library function and.
> testFactors :: Int -> Bool
> testFactors n = error "Define me!"Problem 2: Merge Sort
In this problem, you will implement the standard merge sort algorithm.
The function mergeSort splits the input list at halves, recursively sorts both halves and merges the two sorted halves.
> mergeSort :: [Int] -> [Int]
> mergeSort [] = []
> mergeSort [x] = [x]
> mergeSort xs = merge (mergeSort ys) (mergeSort zs)
> where
> (ys,zs) = splitHalf xs- List splitting: Define the function
splitHalf xsthat split the input list into two sublists. For example:
splitHalf [1..4] == ([1,2],[3,4])
splitHalf [1..5] == ([1,2],[3,4,5])Hint: You can use the function splitAt.
> splitHalf :: [a] -> ([a],[a])
> splitHalf xs = error "Define me!"- Merging: Define the function
mergethat merges two sorted lists. For example,
merge [1, 5, 9] [2,8] == [1,2,5,8,9]Your definition should satisfy the below type signature that reads as follows: The input lists contain elements of type a that satisfies the constraint Ord. This constraint lets you use any of the standard following comparison operators on elements of the input lists
(<), (<=), (>), (>=) :: Ord a => a -> a -> Bool > merge :: Ord a => [a] -> [a] -> [a]
> merge xs ys = error "Define me!"- Raising the Ord constraint: Haskell defines the data type
Orderingas follows:
data Ordering = LT | EQ | GTto represent less, equal, and greater comparison respectively. Moreover the library function compare is defined so that
compare x y == LT <=> x < y
compare x y == EQ <=> x == y
compare x y == GT <=> x > yRedefine the merge and mergeSort functions to take an explicit comparison argument. That is, complete the definitions below so that
mergeBy compare xs == merge xs > mergeBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
> mergeBy cmp xs ys = error "Define me!"
>
> mergeSortBy :: (a -> a -> Ordering) -> [a] -> [a] -> [a]
> mergeSortBy cmp xs ys = error "Define me!"Problem 3: Fancy Sort
Merge sort is an efficient sorting algorithm, but it does not perform well when the input list is already sorted! The goal of this problem is to implement a different sorting algorithm, fancySort that performs excellent when the list is already increasing or even decreasing.
The idea behind fancySort is the following.
- First, grap all the sorted sequences that appear in the input list and
- then, merge all the sorted sublists together.
> fancySort :: Ord a => [a] -> [a]
> fancySort = mergeAll . sequences- Merging: Define the function
mergeAllthat uses themergefrom Problem 2 to merge a list of sorted lists into a single sorted list. For example,
mergeAll [[1, 5, 9], [2,8], [7]] == [1,2,5,7,8,9]> mergeAll :: Ord a => [[a]] -> [a]
> mergeAll xs = error "Define me!"The fancySort function creates the sorted sequences as follows. If the input list (a:b:xs) looks like ascending, that is a < b, then we call ascending' with the “accumulated” sorted list [a] and a “pivot” element b with the goal to collect the rest of the ascending list. Otherwise we call descending with dual arguments and goals. For example,
sequences [1, 2, 3, 2, 1] == [[1,2,3],[1,2],[]]
sequences [1, 2, 3, 4, 5] == [[1,2,3,4,5],[]]
sequences [5, 4, 3, 2, 1] == [[1,2,3,4,5],[]]> sequences :: Ord a => [a] -> [[a]]
> sequences (a:b:xs)
> | a < b = ascending' b [a] xs
> | otherwise = descending b [a] xs
> sequences xs = [xs]The function ascending' a as (b:bs) takes as input the pivot element a, the increasing accumulator as, and the unsorted list xs. If the pivot is less than b, it puts the pivot in the head of the accumulator and recurses, otherwise, it returns the first sorted sequence reverse(a:as) and calls back to sequences to construct the next descending sequence.
> ascending' :: Ord a => a -> [a] -> [a] -> [[a]]
> ascending' a as (b:bs)
> | a < b = ascending' b (a:as) bs
> ascending' a as bs = (reverse(a:as)):sequences bs- Define descending: Follow the definition of
ascending'to definedescendingthat returns the first descending sequence it finds and calls back tosequencesfor the rest. For example,
descending 4 [] [3, 2, 1] == [[1,2,3,4],[]]
descending 0 [] [3, 2, 1] == [[0],[1,2,3],[]]
descending 0 [] [1, 2, 3, 2, 1] == [[0],[1,2,3],[1,2],[]]Note that descending should only find strictly descending sequences, i.e.
descending 1 [] [1, 2] == [[1],[1,2],[]]> descending :: Ord a => a -> [a] -> [a] -> [[a]]
> descending a as bs = error "Define me!"- (Difficult) Optimize ascending: Ascending on an increasing list gives us back exactly one sequence:
ascending' 0 [] [1, 2, 3, 4] == [[0,1,2,3,4],[]]but to do so, the ascending' definition imposes a huge performance leak as it has to reverse the returning list. Define a higher order ascending that satisfies the below type signature, does not reverse the returning sequence and still behaves like ascending' in that
forall xs, a: ascending a id xs == ascending' a [] xs where id is the identity functions.
Restrictions: Increasing lists should be accessed only once! You are not allowed to use reverse, (++), or any other list operations. Define ascending only by using
one comparison,
recursive calls to
ascendingandsequences,function application and abstraction (i.e., lambda),
the list data constructors (
[]and(:)),and no intermediate helper functions.
> ascending :: Ord a => a -> ([a] -> [a]) -> [a] -> [[a]]
> ascending a as bs = error "Define me!"Finally, replace the call to ascending' with ascending in the sequences definition.
Problem 4: Tic-Tac-Toe
- Step 1: Download the tic-tac-toe game.
git clone https://github.com/nikivazou/tic-tac-toe.git- Step 2: Install the game and play against the random strategy.
cd tic-tac-toe/classic/
stack install
tic-tac-toeStep 3: Follow the description in the src/Player/MinMax.lhs file.
Step 4: Import and call your player. In src/TicTacToe.hs add
import Player.MinMax (playerMinMax)then in src/TicTacToe.hs#L11 replace
player1 = playerHuman "Me!"with
player1 = playerMinMaxand make sure that your playerMinMax always wins the computer.