advent/src/Util.md

296 lines
5.9 KiB
Markdown

# Utility functions
This module contains functions that extend the functionality of standard data
types, other types of utility functionality get their own module
```idris
module Util
import Data.SortedSet
import Data.String
import Data.List.Lazy
import Data.List1
import Data.Vect
import Data.Fin
%default total
```
## Functions
### repeatN
Recursively applies `f` to `seed` N times
```idris
export
repeatN : (times : Nat) -> (f : a -> a) -> (seed : a) -> a
repeatN 0 f seed = seed
repeatN (S times') f seed = repeatN times' f (f seed)
```
## Either
```idris hide
namespace Either
```
### mapLeft
Applies a function to the contents of a `Left` if the value of the given
`Either` is actually a `Left`, otherwise, leaves `Right`s untouched.
```idris
export
mapLeft : (f : a -> b) -> Either a c -> Either b c
mapLeft f (Left x) = Left (f x)
mapLeft f (Right x) = Right x
```
## List
```idris hide
namespace List
```
### contains
Returns `True` if the list contains the given value
```idris
export
contains : Eq a => a -> List a -> Bool
contains x [] = False
contains x (y :: xs) =
if x == y
then True
else contains x xs
```
### rotations
Provides all the 'rotations' of a list.
Example:
```
rotations [1, 2, 3] == [[1, 2, 3], [3, 1, 2], [2, 3, 1]]
```
```idris
export
rotations : List a -> List (List a)
rotations xs = rotations' (length xs) xs []
where
rotations' : Nat -> List a -> (acc : List (List a)) -> List (List a)
rotations' 0 xs acc = acc
rotations' (S k) [] acc = acc
rotations' (S k) (x :: xs) acc =
let next = xs ++ [x]
in rotations' k next (next :: acc)
```
### permutations
Lazily generate all of the permutations of a list
```idris
export
permutations : List a -> LazyList (List a)
permutations [] = pure []
permutations xs = do
(head, tail) <- select xs
tail <- permutations (assert_smaller xs tail)
pure $ head :: tail
where
consSnd : a -> (a, List a) -> (a, List a)
consSnd x (y, xs) = (y, x :: xs)
select : List a -> LazyList (a, List a)
select [] = []
select (x :: xs) = (x, xs) :: map (consSnd x) (select xs)
```
## Vect
```idris hide
namespace Vect
```
### permutations
Lazily generate all the permutations of a Vect
```idris
export
permutations : Vect n a -> LazyList (Vect n a)
permutations [] = []
permutations [x] = [[x]]
permutations (x :: xs) = do
(head, tail) <- select (x :: xs)
tail <- permutations tail
pure $ head :: tail
where
consSnd : a -> (a, Vect m a) -> (a, Vect (S m) a)
consSnd x (y, xs) = (y, x :: xs)
select : Vect (S m) a -> LazyList (a, Vect m a)
select [y] = [(y, [])]
select (y :: (z :: ys)) =
(y, z :: ys) :: map (consSnd y) (select (z :: ys))
```
## Fin
```idris hide
namespace Fin
```
```idris
||| Decriment a Fin, wrapping on overflow
export
unfinS : {n : _} -> Fin n -> Fin n
unfinS FZ = last
unfinS (FS x) = weaken x
```
## Vectors
Define some operations for pairs of numbers, treating them roughly like vectors
### Addition and Subtraction
```idris hide
export infixl 8 >+<
export infixl 8 >-<
namespace Pair
```
```idris
public export
(>+<) : Num a => (x, y : (a, a)) -> (a, a)
(x_1, x_2) >+< (y_1, y_2) = (x_1 + y_1, x_2 + y_2)
public export
(>-<) : Neg a => (x, y : (a, a)) -> (a, a)
(x_1, x_2) >-< (y_1, y_2) = (x_1 - y_1, x_2 - y_2)
```
## Set
Extend `Data.SortedSet`
```idris hide
namespace Set
```
### length
Count the number of elements in a sorted set
```idris
export
length : SortedSet k -> Nat
length x = foldl (\acc, e => acc + 1) 0 x
```
## String
Extend `Data.String`
```idris hide
namespace String
```
### isSubstr
Returns `True` if `needle` is a substring of `haystack`
We first check to see if the needle is a prefix of the top level haystack,
before calling into a tail recursive helper function that peels one character
off of the string at a time, checking if the needle is a prefix at each step.
```idris
export
isSubstr : (needle, haystack : String) -> Bool
isSubstr needle haystack =
if needle `isPrefixOf` haystack
then True
else isSubstr' haystack
where
isSubstr' : String -> Bool
isSubstr' str with (asList str)
isSubstr' "" | [] = False
isSubstr' (strCons c str) | (c :: x) =
if needle `isPrefixOf` str
then True
else isSubstr' str | x
```
## Lazy List
### Cartesian product
```idris hide
namespace LazyList
```
Lazily take the cartesian product of two foldables
```idris
export
cartProd : Foldable a => Foldable b => a e -> b f -> LazyList (e, f)
cartProd x y =
let y = foldToLazy y
in foldr (\e, acc => combine e y acc) [] x
where
foldToLazy : Foldable a' => a' e' -> LazyList e'
foldToLazy x = foldr (\e, acc => e :: acc) [] x
combine : e -> LazyList f -> LazyList (e, f) -> LazyList (e, f)
combine x [] rest = rest
combine x (y :: ys) rest = (x, y) :: combine x ys rest
```
### Concat
Lazily concatenate a LazyList of LazyLists
```idris
export
lazyConcat : LazyList (LazyList a) -> LazyList a
lazyConcat [] = []
lazyConcat (x :: xs) = x ++ lazyConcat xs
```
### Group
Lazily group a LazyList
```idris
export
lazyGroup : Eq a => LazyList a -> LazyList (List1 a)
lazyGroup [] = []
lazyGroup (x :: xs) = lazyGroup' xs x (x ::: [])
where
lazyGroup' : LazyList a -> (current : a) -> (acc : List1 a)
-> LazyList (List1 a)
lazyGroup' [] current acc = [acc]
lazyGroup' (y :: ys) current acc@(head ::: tail) =
if y == current
then lazyGroup' ys current (head ::: (y :: tail))
else acc :: lazyGroup (y :: ys)
```
### length
Calculate the length of a LazyList
```idris
export
length : LazyList a -> Nat
length = length' 0
where
length' : Nat -> LazyList a -> Nat
length' k [] = k
length' k (x :: xs) = length' (S k) xs
```