
* In OTP-27 else is made a keyword, so needs quouting * Fix List.aes - remove unnecessary self-qualification * Changelog
317 lines
9.8 KiB
Plaintext
317 lines
9.8 KiB
Plaintext
include "ListInternal.aes"
|
||
|
||
namespace List =
|
||
|
||
function is_empty(l : list('a)) : bool = switch(l)
|
||
[] => true
|
||
_ => false
|
||
|
||
function first(l : list('a)) : option('a) = switch(l)
|
||
[] => None
|
||
h::_ => Some(h)
|
||
|
||
function tail(l : list('a)) : option(list('a)) = switch(l)
|
||
[] => None
|
||
_::t => Some(t)
|
||
|
||
function last(l : list('a)) : option('a) = switch(l)
|
||
[] => None
|
||
[x] => Some(x)
|
||
_::t => last(t)
|
||
|
||
function drop_last(l : list('a)) : option(list('a)) = switch(l)
|
||
[] => None
|
||
_ => Some(drop_last_unsafe(l))
|
||
|
||
function drop_last_unsafe(l : list('a)) : list('a) = switch(l)
|
||
[_] => []
|
||
h::t => h::drop_last_unsafe(t)
|
||
[] => abort("drop_last_unsafe: list empty")
|
||
|
||
|
||
function contains(e : 'a, l : list('a)) = switch(l)
|
||
[] => false
|
||
h::t => h == e || contains(e, t)
|
||
|
||
/** Finds first element of `l` fulfilling predicate `p` as `Some` or `None`
|
||
* if no such element exists.
|
||
*/
|
||
function find(p : 'a => bool, l : list('a)) : option('a) = switch(l)
|
||
[] => None
|
||
h::t => if(p(h)) Some(h) else find(p, t)
|
||
|
||
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
|
||
*/
|
||
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0)
|
||
private function find_indices_( p : 'a => bool
|
||
, l : list('a)
|
||
, n : int
|
||
) : list(int) = switch(l)
|
||
[] => []
|
||
h::t =>
|
||
let rest = find_indices_(p, t, n+1)
|
||
if(p(h)) n::rest else rest
|
||
|
||
function nth(n : int, l : list('a)) : option('a) =
|
||
switch(l)
|
||
[] => None
|
||
h::t => if(n == 0) Some(h) else nth(n-1, t)
|
||
|
||
/* Unsafe version of `nth` */
|
||
function get(n : int, l : list('a)) : 'a =
|
||
switch(l)
|
||
[] => abort(if(n < 0) "Negative index get" else "Out of index get")
|
||
h::t => if(n == 0) h else get(n-1, t)
|
||
|
||
|
||
function length(l : list('a)) : int = length_(l, 0)
|
||
private function length_(l : list('a), acc : int) : int = switch(l)
|
||
[] => acc
|
||
_::t => length_(t, acc + 1)
|
||
|
||
|
||
/** Creates an ascending sequence of all integer numbers
|
||
* between `a` and `b` (including `a` and `b`)
|
||
*/
|
||
function from_to(a : int, b : int) : list(int) = [a..b]
|
||
|
||
/** Creates an ascending sequence of integer numbers betweeen
|
||
* `a` and `b` jumping by given `step`. Includes `a` and takes
|
||
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
|
||
*/
|
||
function from_to_step(a : int, b : int, s : int) : list(int) =
|
||
require(s > 0, "List.from_to_step: non-positive step")
|
||
from_to_step_(a, b - (b-a) mod s, s, [])
|
||
private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) =
|
||
if(b < a) acc
|
||
else from_to_step_(a, b - s, s, b::acc)
|
||
|
||
|
||
/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
|
||
*/
|
||
function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l)
|
||
private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||
switch(l)
|
||
[] => abort("replace_at overflow")
|
||
h::t => if (n == 0) e::t
|
||
else h::replace_at_(n-1, e, t)
|
||
|
||
/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
|
||
*/
|
||
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l)
|
||
private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||
if (n == 0) e::l
|
||
else switch(l)
|
||
[] => abort("insert_at overflow")
|
||
h::t => h::insert_at_(n-1, e, t)
|
||
|
||
/** Assuming that cmp represents `<` comparison, inserts `x` before
|
||
* the first element in the list `l` which is greater than it
|
||
*/
|
||
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
|
||
switch(l)
|
||
[] => [x]
|
||
h::t =>
|
||
if(cmp(x, h)) // x < h
|
||
x::l
|
||
else
|
||
h::insert_by(cmp, x, t)
|
||
|
||
|
||
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
|
||
[] => nil
|
||
h::t => cons(h, foldr(cons, nil, t))
|
||
|
||
function foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b = switch(l)
|
||
[] => acc
|
||
h::t => foldl(rcons, rcons(acc, h), t)
|
||
|
||
function foreach(l : list('a), f : 'a => unit) : unit =
|
||
switch(l)
|
||
[] => ()
|
||
e::l' =>
|
||
f(e)
|
||
foreach(l', f)
|
||
|
||
function reverse(l : list('a)) : list('a) = reverse_(l, [])
|
||
private function reverse_(l : list('a), acc : list('a)) : list('a) = switch(l)
|
||
[] => acc
|
||
h::t => reverse_(t, h::acc)
|
||
|
||
function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l)
|
||
[] => []
|
||
h::t => f(h)::map(f, t)
|
||
|
||
/** Effectively composition of `map` and `flatten`
|
||
*/
|
||
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
||
ListInternal.flat_map(f, l)
|
||
|
||
function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||
[] => []
|
||
h::t =>
|
||
let rest = filter(p, t)
|
||
if(p(h)) h::rest else rest
|
||
|
||
/** Take up to `n` first elements
|
||
*/
|
||
function take(n : int, l : list('a)) : list('a) =
|
||
if(n < 0) abort("Take negative number of elements") else take_(n, l)
|
||
private function take_(n : int, l : list('a)) : list('a) =
|
||
if(n == 0) []
|
||
else switch(l)
|
||
[] => []
|
||
h::t => h::take_(n-1, t)
|
||
|
||
/** Drop up to `n` first elements
|
||
*/
|
||
function drop(n : int, l : list('a)) : list('a) =
|
||
if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
|
||
private function drop_(n : int, l : list('a)) : list('a) =
|
||
if (n == 0) l
|
||
else switch(l)
|
||
[] => []
|
||
_::t => drop_(n-1, t)
|
||
|
||
/** Get the longest prefix of a list in which every element
|
||
* matches predicate `p`
|
||
*/
|
||
function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||
[] => []
|
||
h::t => if(p(h)) h::take_while(p, t) else []
|
||
|
||
/** Drop elements from `l` until `p` holds
|
||
*/
|
||
function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||
[] => []
|
||
h::t => if(p(h)) drop_while(p, t) else l
|
||
|
||
/** Splits list into two lists of elements that respectively
|
||
* match and don't match predicate `p`
|
||
*/
|
||
function partition(p : 'a => bool, lst : list('a)) : (list('a) * list('a)) = switch(lst)
|
||
[] => ([], [])
|
||
h::t =>
|
||
let (l, r) = partition(p, t)
|
||
if(p(h)) (h::l, r) else (l, h::r)
|
||
|
||
function flatten(l : list(list('a))) : list('a) = switch(l)
|
||
[] => []
|
||
h::t => h ++ flatten(t)
|
||
|
||
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
||
[] => true
|
||
h::t => if(p(h)) all(p, t) else false
|
||
|
||
function any(p : 'a => bool, l : list('a)) : bool = switch(l)
|
||
[] => false
|
||
h::t => if(p(h)) true else any(p, t)
|
||
|
||
function sum(l : list(int)) : int = switch(l)
|
||
[] => 0
|
||
h::t => h + sum(t)
|
||
|
||
function product(l : list(int)) : int = switch(l)
|
||
[] => 1
|
||
h::t => h * sum(t)
|
||
|
||
/** Zips two list by applying bimapping function on respective elements.
|
||
* Drops the tail of the longer list.
|
||
*/
|
||
private function zip_with( f : ('a, 'b) => 'c
|
||
, l1 : list('a)
|
||
, l2 : list('b)
|
||
) : list('c) = switch ((l1, l2))
|
||
(h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2)
|
||
_ => []
|
||
|
||
/** Zips two lists into list of pairs.
|
||
* Drops the tail of the longer list.
|
||
*/
|
||
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
|
||
|
||
function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l)
|
||
[] => ([], [])
|
||
(h1, h2)::t =>
|
||
let (t1, t2) = unzip(t)
|
||
(h1::t1, h2::t2)
|
||
|
||
|
||
/** Merges two sorted lists using `lt` comparator
|
||
*/
|
||
function
|
||
merge : (('a, 'a) => bool, list('a), list('a)) => list('a)
|
||
merge(lt, x::xs, y::ys) =
|
||
if(lt(x, y)) x::merge(lt, xs, y::ys)
|
||
else y::merge(lt, x::xs, ys)
|
||
merge(_, [], ys) = ys
|
||
merge(_, xs, []) = xs
|
||
|
||
|
||
/** Mergesort inspired by
|
||
* https://hackage.haskell.org/package/base-4.14.1.0/docs/src/Data.OldList.html#sort
|
||
*/
|
||
function
|
||
sort : (('a, 'a) => bool, list('a)) => list('a)
|
||
sort(_, []) = []
|
||
sort(lt, l) =
|
||
merge_all(lt, monotonic_subs(lt, l))
|
||
|
||
/** Splits list into compound increasing sublists
|
||
*/
|
||
private function
|
||
monotonic_subs : (('a, 'a) => bool, list('a)) => list(list('a))
|
||
monotonic_subs(lt, x::y::rest) =
|
||
if(lt(y, x)) desc(lt, y, [x], rest)
|
||
else asc(lt, y, [x], rest)
|
||
monotonic_subs(_, l) = [l]
|
||
|
||
/** Extracts the longest descending prefix and proceeds with monotonic split
|
||
*/
|
||
private function
|
||
desc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||
desc(lt, x, acc, h::t) =
|
||
if(lt(x, h)) (x::acc) :: monotonic_subs(lt, h::t)
|
||
else desc(lt, h, x::acc, t)
|
||
desc(_, x, acc, []) = [x::acc]
|
||
|
||
/** Extracts the longest ascending prefix and proceeds with monotonic split
|
||
*/
|
||
private function
|
||
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||
asc(lt, x, acc, h::t) =
|
||
if(lt(h, x)) reverse(x::acc) :: monotonic_subs(lt, h::t)
|
||
else asc(lt, h, x::acc, t)
|
||
asc(_, x, acc, []) = [reverse(x::acc)]
|
||
|
||
/** Merges list of sorted lists
|
||
*/
|
||
private function
|
||
merge_all : (('a, 'a) => bool, list(list('a))) => list('a)
|
||
merge_all(_, [part]) = part
|
||
merge_all(lt, parts) = merge_all(lt, merge_pairs(lt, parts))
|
||
|
||
/** Single round of `merge_all` – pairs of lists in a list of list
|
||
*/
|
||
private function
|
||
merge_pairs : (('a, 'a) => bool, list(list('a))) => list(list('a))
|
||
merge_pairs(lt, x::y::rest) = merge(lt, x, y) :: merge_pairs(lt, rest)
|
||
merge_pairs(_, l) = l
|
||
|
||
|
||
/** Puts `delim` between every two members of the list
|
||
*/
|
||
function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l)
|
||
[] => []
|
||
[e] => [e]
|
||
h::t => h::delim::intersperse(delim, t)
|
||
|
||
/** Effectively a zip with an infinite sequence of natural numbers
|
||
*/
|
||
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0)
|
||
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l)
|
||
[] => []
|
||
h::t => (n, h)::enumerate_(t, n + 1)
|