Assignment 1 – Smooth permutations
Before you begin
We are using GitHub Classrooms for this assignment. Before you begin with the assignment, please link your GitHub account to the AFP classroom and create a repo for this assignment. You can do so by following this link.
The deadline for this assignment is is 2025-02-15 @ 23:59.
The deadline for this assignment is is 2025-02-17 @ 12:00 (extended following confusion).
More information about how to conduct the peer review can be found here.
Introduction
In this assignment we want to build a library to generate smooth permutations. Given a list of integers xs and an integer d, a smooth permutation of xs with maximum distance d is a permutation in which the difference of any two consecutive elements is less than d.
A naïve implementation just generates all the permutations of a list,
split [] = []
split (x:xs) = (x, xs) : [(y, x:ys) | (y, ys) <- split xs]
perms [] = [[]]
perms xs = [(v:p) | (v, vs) <- split xs, p <- perms vs]
and then filters out those which are smooth,
smooth n (x:y:ys) = abs (y - x) < n && smooth n (y:ys)
smooth _ _ = True
smoothPerms :: Int -> [Int] -> [[Int]]
smoothPerms n xs = filter (smooth n) (perms xs)
Exercise 1 – Packaging and documentation (1 pt)
- Create a library
smoothieswhich exportspermsandsmoothPermsfrom a moduleSmoothPermsSlow. You should be able to compile the package by runningcabal buildin it. - Document the exported functions using Haddock.
Exercise 2 – Testsuite (1 pt)
- Write a
SmoothPermsTestmodule with a comprehensive set of properties to check thatsmoothPermsworks correctly. - Integrate your testsuite with Cabal using
tasty(here is how you do so). Thetastytool lets you run your testsuite using cabal. The section on ‘Project organization and integration with Cabal’ in the README gives you some more information about how to add this to your project.
Exercise 3 – Implementation with trees (3 pt)
The initial implementation of smoothPerms is very expensive. A better approach is to build a tree, for which it holds that each path from the root to a leaf corresponds to one of the possible permutations, next prune this tree such that only smooth paths are represented, and finally use this tree to generate all the smooth permutations from. Expose this new implementation in a new SmoothPermsTree module.
- Define a data type
PermTreeto represented a permutation tree. - Define a function
listToPermTreewhich maps a list onto this tree. -
Define a function
permTreeToPermswhich generates all permutations represented by a tree.At this point the
permsfunctions given above should be the composition oflistToPermTreeandpermTreeToPerms. - Define a function
pruneSmooth, which leaves only smooth permutations in the tree. - Redefine the function
smoothPerms.
Integrate this module in the testsuite you developed in the previous exercise.
Exercise 4 – Unfolds (3 pts)
Recall the definition of unfoldr for lists,
unfoldr :: (s -> Maybe (a, s)) -> s -> [a]
unfoldr next x = case next x of
Nothing -> []
Just (y, r) -> y : unfoldr next r
We can define an unfold function for binary trees as well:
data Tree a = Leaf a | Node (Tree a) (Tree a)
deriving Show
unfoldTree :: (s -> Either a (s, s)) -> s -> Tree a
unfoldTree next x = case next x of
Left y -> Leaf y
Right (l, r) -> Node (unfoldTree next l) (unfoldTree next r)
Define the following functions in a new module UnfoldUtils, which should not be exposed by your package. Define the functions using unfoldr or unfoldTree, as required.
iterate :: (a -> a) -> a -> [a]. The calliterate f xgenerates the infinite list[x, f x, f (f x), ...].map :: (a -> b) -> [a] -> [b].balanced :: Int -> Tree (), which generates a balanced binary tree of the given height.sized :: Int -> Tree Int, which generates any tree with the given number of nodes. Each leaf in the returned tree should have a unique label.
Define a new module SmoothPermsUnfold with an unfoldPermTree function which generates a PermTree as defined in the previous exercise. Then use that unfoldPermTree to implement a new version of listToPermTree and smoothPerms.
Recap of modules
By the end of exercise 4, you should have a package with the following modules:
SmoothPermsSlow, exposed, with the initial slow implementation.SmoothPermsTest, which contains the QuickCheck tests.SmoothPermsTree, exposed, with thePermsTree-based implementation.UnfoldUtils, hidden.SmoothPermsUnfold, exposed, with theunfold-based implementation.
Exercise 5 – Proofs (2 pts)
Write the following proofs as comments in the UnfoldUtils module.
- Prove using induction and equational reasoning that the version of
mapyou defined usingunfoldrcoincides with the definition ofmapby recursion. -
We define the
sizeof a binary tree as the number of internal nodes.size (Leaf _) = 0 size (Node l r) = 1 + size l + size rWhat is the
sizeof a balanced tree as generated bybalanced? Prove your result using induction and equational reasoning.