From 13bc8212117d2cf3812eeff7df8a349f5990dc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Fri, 7 Feb 2020 19:51:12 +0100 Subject: [PATCH] Optimize stdlib (#215) --- priv/stdlib/List.aes | 155 +++++++++++++++++++++-------------------- priv/stdlib/Option.aes | 20 +++--- 2 files changed, 91 insertions(+), 84 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 64c4fd5..98fd4fe 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -19,18 +19,28 @@ namespace List = [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 find(p : 'a => bool, l : list('a)) : option('a) = switch(l) [] => None h::t => if(p(h)) Some(h) else find(p, t) - function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) + 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 - , acc : list(int) ) : list(int) = switch(l) - [] => reverse(acc) - h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) + [] => [] + 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) @@ -52,39 +62,40 @@ namespace List = function from_to(a : int, b : int) : list(int) = [a..b] - function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, []) - private function from_to_step_(a, b, s, acc) = - if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc) + function from_to_step(a : int, b : int, s : int) : list(int) = + 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), acc : 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) reverse(e::acc) ++ t - else replace_at_(n-1, e, t, h::acc) + 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), acc : list('a)) : list('a) = - if (n == 0) reverse(e::acc) ++ l + 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 => insert_at_(n-1, e, t, h::acc) + h::t => h::insert_at_(n-1, e, t) function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = - insert_by_(cmp, x, l, []) - private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(x::acc) + [] => [x] h::t => if(cmp(x, h)) // x < h - reverse(acc) ++ (x::l) + x::l else - insert_by_(cmp, x, t, h::acc) + h::insert_by(cmp, x, t) function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) @@ -102,43 +113,46 @@ namespace List = f(e) foreach(l', f) - function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l) + 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) = map_(f, l, []) - private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l) - [] => reverse(acc) - h::t => map_(f, t, f(h)::acc) + function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l) + [] => [] + h::t => f(h)::map(f, t) 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) = filter_(p, l, []) - private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => filter_(p, t, if(p(h)) h::acc else acc) + 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 `n` first elements */ + /* 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), acc : list('a)) : list('a) = - if(n == 0) reverse(acc) + 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) - [] => reverse(acc) - h::t => take_(n-1, t, h::acc) + [] => [] + h::t => h::take_(n-1, t) - /* Drop `n` first elements */ + /* Drop up to `n` first elements */ function drop(n : int, l : list('a)) : list('a) = - if(n < 0) abort("Drop negative number of elements") - elif (n == 0) l + 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) [] => [] - h::t => drop(n-1, t) + h::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) = take_while_(p, l, []) - private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) + 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) @@ -146,17 +160,15 @@ namespace List = 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, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], []) - private function partition_( p : 'a => bool - , l : list('a) - , acc_t : list('a) - , acc_f : list('a) - ) : (list('a) * list('a)) = switch(l) - [] => (reverse(acc_t), reverse(acc_f)) - h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) + function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l) + [] => ([], []) + h::t => + let (l, r) = partition(p, t) + if(p(h)) (h::l, r) else (l, h::r) - - function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) + 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 @@ -172,25 +184,21 @@ namespace List = /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ - function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, []) - private function zip_with_( f : ('a, 'b) => 'c + private function zip_with( f : ('a, 'b) => 'c , l1 : list('a) , l2 : list('b) - , acc : list('c) ) : list('c) = switch ((l1, l2)) - (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) - _ => reverse(acc) + (h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2) + _ => [] /* Zips two lists into list of pairs. Drops longer tail. */ 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) = unzip_(l, [], []) - private function unzip_( l : list('a * 'b) - , acc_l : list('a) - , acc_r : list('b) - ) : (list('a) * list('b)) = switch(l) - [] => (reverse(acc_l), reverse(acc_r)) - (left, right)::t => unzip_(t, left::acc_l, right::acc_r) + function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l) + [] => ([], []) + (h1, h2)::t => + let (t1, t2) = unzip(t) + (h1::t1, h2::t2) // TODO: Improve? @@ -200,15 +208,14 @@ namespace List = (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) - function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) - private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - [e] => reverse(e::acc) - h::t => intersperse_(delim, t, delim::h::acc) + function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l) + [] => [] + [e] => [e] + h::t => h::delim::intersperse(delim, t) - function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, []) - private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l) - [] => reverse(acc) - h::t => enumerate_(t, n + 1, (n, h)::acc) + 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) diff --git a/priv/stdlib/Option.aes b/priv/stdlib/Option.aes index 6ebf98c..9647230 100644 --- a/priv/stdlib/Option.aes +++ b/priv/stdlib/Option.aes @@ -53,17 +53,17 @@ namespace Option = None => [] Some(x) => [x] - function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) - private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) - [] => List.reverse(acc) - None::t => filter_options_(t, acc) - Some(x)::t => filter_options_(t, x::acc) + function filter_options(l : list(option('a))) : list('a) = switch(l) + [] => [] + None::t => filter_options(t) + Some(x)::t => x::filter_options(t) - function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) - private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) - [] => Some(List.reverse(acc)) - None::t => None - Some(x)::t => seq_options_(t, x::acc) + function seq_options(l : list (option('a))) : option (list('a)) = switch(l) + [] => Some([]) + None::_ => None + Some(x)::t => switch(seq_options(t)) + None => None + Some(st) => Some(x::st) function choose(o1 : option('a), o2 : option('a)) : option('a) =