February 2021
Purely Functional Data Structures (chapters 1 & 2), Chris Okasaki, Cambridge University Press, 1998.
Introduction to Functional Programming (Chapter 9.3), Richard Bird, Phil Wadler, 1988.
Haskell lists are built from constructors []
and :
.
Pattern matching destructs one constructor at a time.
length xs
takes \(O(n)\) timelength xs
):
) allocated for each stepxs ++ ys
fully takes \(O(n)\) time/spacelength xs
)Replace the \(n\)-th element in a list:
replace :: Int -> a -> [a] -> [a]
replace 0 v (x:xs) = v:xs
replace n v (x:xs) = x:replace (n-1) v xs
Sample:
data Tree a
= Branch a (Tree a) (Tree a)
-- value, left and right subtrees
| Empty
-- insertion preserving ordering
insert :: Ord a => a -> Tree a -> Tree a
insert v Empty
= Branch v Empty Empty
insert v (Branch x l r)
| v <= x = Branch x (insert v l) r
| v > x = Branch x l (insert v r)
Sharing can be useful even within a single data structure.
Example (from Data.List):
E.g.
By sharing suffixes, tails
uses \(O(n)\) time and space.
What is the complexity of the naive reverse function?
What is the complexity of reverse
with accumulator?
reverse :: [a] -> [a]
reverse xs = revAcc xs []
where
revAcc :: [a] -> [a] -> [a]
revAcc [] ys = ys
revAcc (x:xs) ys = revAcc xs (x:ys)
What is the complexity of tail
and init
?
(NB: both are undefined for []
.)
k
to values v
(also called dictionaries.)empty :: Map k v
insert :: ... => k -> v -> Map k v -> Map k v
lookup :: ... => k -> Map k v -> Maybe v
-- NB: need some class constraints
-- for efficient implementations
Using binary search trees:
Hence: most operations need an Ord k
constraint on keys (but not values).
lookup :: Ord k => k -> Map k v -> Maybe v
lookup k Empty = Nothing
lookup k (Branch k' v left right)
| k == k' = Just v
| k < k' = lookup k left
| otherwise = lookup k right
Implement the remaining operations:
empty :: Map k v
-- construct an empty map;
-- does *not* need the Ord constraint
insert :: Ord k => k -> v -> Map k v -> Map k v
-- insert or update a key
Implement some extra operations:
fromList :: Ord k => [(k,v)] -> Map k v
-- build map from a list of pairs
keys :: Map k v -> [k]
-- get all keys in a map
assocs :: Map k v -> [(k,v)]
-- get the list of pairs in a map
NB: the last two do not need the Ord
constraint (can you explain why?)
Implement an instance for the Show
class:
Sugestion: show maps as lists of pairs:
> show (fromList [('a',1),('b',2)])
"fromList [('a',1),('b',2)]"
Exercise: modify the implemention to ensure balance — e.g. using AVL trees.
base
generic list functions (Prelude and Data.List)
containers
ordering-based containers (e.g. sets, maps)
unordered-containers
hashing-based containers (e.g. hash maps)
vector
purely-functional arrays with efficent loop fusion optimisations
text
efficent packed Unicode text