diff options
author | Louis Pilfold <louis@lpil.uk> | 2021-07-18 17:33:43 +0100 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2021-07-18 17:33:43 +0100 |
commit | 3d287285eb8a1e824a400d01afa543454eff7b44 (patch) | |
tree | 75f0330233b580f12f11b1bd25159e6a2017fa33 | |
parent | 6b449f10ee19c3012cfb8c33d9c687acd4d0d303 (diff) | |
download | gleam_stdlib-3d287285eb8a1e824a400d01afa543454eff7b44.tar.gz gleam_stdlib-3d287285eb8a1e824a400d01afa543454eff7b44.zip |
JS list support
-rw-r--r-- | CHANGELOG.md | 3 | ||||
-rw-r--r-- | src/gleam/float.gleam | 197 | ||||
-rw-r--r-- | src/gleam/int.gleam | 234 | ||||
-rw-r--r-- | src/gleam/list.gleam | 2838 | ||||
-rw-r--r-- | test/gleam/list_test.gleam | 1158 |
5 files changed, 2251 insertions, 2179 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 2440c2e..d84fb76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ## Unreleased - The `os` module has been moved to the `gleam_os` library. -- The `bool`, `order`, and `pair` modules now support JavaScript compilation. +- The `bool`, `list`, `order`, and `pair` modules now support JavaScript + compilation. - The `map.update` function now uses `Option` rather than `Result`. - The `iterator` module gains the `fold_until` function. diff --git a/src/gleam/float.gleam b/src/gleam/float.gleam index ed1d3ab..5938be0 100644 --- a/src/gleam/float.gleam +++ b/src/gleam/float.gleam @@ -1,6 +1,7 @@ +import gleam/order.{Order} + if erlang { import gleam/string_builder - import gleam/order.{Order} pub type Float = Float @@ -29,67 +30,69 @@ if erlang { |> string_builder.from_float |> string_builder.to_string } +} - /// Restricts a Float between a lower and upper bound - /// - /// ## Examples - /// - /// ``` - /// > clamp(1.2, min: 1.4, max: 1.6) - /// 1.4 - /// ``` - /// - pub fn clamp(n: Float, min min_bound: Float, max max_bound: Float) -> Float { - n - |> min(max_bound) - |> max(min_bound) - } +/// Restricts a Float between a lower and upper bound +/// +/// ## Examples +/// +/// ``` +/// > clamp(1.2, min: 1.4, max: 1.6) +/// 1.4 +/// ``` +/// +pub fn clamp(n: Float, min min_bound: Float, max max_bound: Float) -> Float { + n + |> min(max_bound) + |> max(min_bound) +} - /// Compares two floats, returning an order. - /// - /// ## Examples - /// > compare(2.0, 2.3) - /// Lt - /// - pub fn compare(a: Float, with b: Float) -> Order { - case a == b { - True -> order.Eq - False -> - case a <. b { - True -> order.Lt - False -> order.Gt - } - } +/// Compares two floats, returning an order. +/// +/// ## Examples +/// > compare(2.0, 2.3) +/// Lt +/// +pub fn compare(a: Float, with b: Float) -> Order { + case a == b { + True -> order.Eq + False -> + case a <. b { + True -> order.Lt + False -> order.Gt + } } +} - /// Compares two floats, returning the smaller of the two. - /// - /// ## Examples - /// - /// > min(2.0, 2.3) - /// 2.0 - /// - pub fn min(a: Float, b: Float) -> Float { - case a <. b { - True -> a - False -> b - } +/// Compares two floats, returning the smaller of the two. +/// +/// ## Examples +/// +/// > min(2.0, 2.3) +/// 2.0 +/// +pub fn min(a: Float, b: Float) -> Float { + case a <. b { + True -> a + False -> b } +} - /// Compares two floats, returning the larger of the two. - /// - /// ## Examples - /// - /// > max(2.0, 2.3) - /// 2.3 - /// - pub fn max(a: Float, b: Float) -> Float { - case a >. b { - True -> a - False -> b - } +/// Compares two floats, returning the larger of the two. +/// +/// ## Examples +/// +/// > max(2.0, 2.3) +/// 2.3 +/// +pub fn max(a: Float, b: Float) -> Float { + case a >. b { + True -> a + False -> b } +} +if erlang { /// Rounds the value to the next highest whole number as a float. /// /// ## Examples @@ -176,55 +179,55 @@ if erlang { False -> Ok(power(number, 0.5)) } } +} - /// Returns the negative of the value provided - /// - /// ## Examples - /// - /// > negate(1.) - /// -1. - /// - pub fn negate(x: Float) -> Float { - -1. *. x - } +/// Returns the negative of the value provided +/// +/// ## Examples +/// +/// > negate(1.) +/// -1. +/// +pub fn negate(x: Float) -> Float { + -1. *. x +} - /// Sums a list of Floats. - /// - /// ## Example - /// - /// > sum([1.0, 2.2, 3.3]) - /// 6.5 - /// - pub fn sum(numbers: List(Float)) -> Float { - numbers - |> do_sum(0.0) - } +/// Sums a list of Floats. +/// +/// ## Example +/// +/// > sum([1.0, 2.2, 3.3]) +/// 6.5 +/// +pub fn sum(numbers: List(Float)) -> Float { + numbers + |> do_sum(0.0) +} - fn do_sum(numbers: List(Float), initial: Float) -> Float { - case numbers { - [] -> initial - [x, ..rest] -> do_sum(rest, x +. initial) - } +fn do_sum(numbers: List(Float), initial: Float) -> Float { + case numbers { + [] -> initial + [x, ..rest] -> do_sum(rest, x +. initial) } +} - /// Multiplies a list of Floats and returns the product. - /// - /// ## Example - /// - /// > product([2.5, 3.2, 4.2]) - /// 33.6 - /// - pub fn product(numbers: List(Float)) -> Float { - case numbers { - [] -> 0. - _ -> do_product(numbers, 1.) - } +/// Multiplies a list of Floats and returns the product. +/// +/// ## Example +/// +/// > product([2.5, 3.2, 4.2]) +/// 33.6 +/// +pub fn product(numbers: List(Float)) -> Float { + case numbers { + [] -> 0. + _ -> do_product(numbers, 1.) } +} - fn do_product(numbers: List(Float), initial: Float) -> Float { - case numbers { - [] -> initial - [x, ..rest] -> do_product(rest, x *. initial) - } +fn do_product(numbers: List(Float), initial: Float) -> Float { + case numbers { + [] -> initial + [x, ..rest] -> do_product(rest, x *. initial) } } diff --git a/src/gleam/int.gleam b/src/gleam/int.gleam index b65d3a3..5c1a588 100644 --- a/src/gleam/int.gleam +++ b/src/gleam/int.gleam @@ -1,6 +1,6 @@ -if erlang { - import gleam/order.{Order} +import gleam/order.{Order} +if erlang { pub type Int = Int @@ -90,135 +90,135 @@ if erlang { |> min(max_bound) |> max(min_bound) } +} - /// Compares two ints, returning an order. - /// - /// ## Examples - /// - /// > compare(2, 3) - /// Lt - /// - /// > compare(4, 3) - /// Gt - /// - /// > compare(3, 3) - /// Eq - /// - pub fn compare(a: Int, with b: Int) -> Order { - case a == b { - True -> order.Eq - False -> - case a < b { - True -> order.Lt - False -> order.Gt - } - } +/// Compares two ints, returning an order. +/// +/// ## Examples +/// +/// > compare(2, 3) +/// Lt +/// +/// > compare(4, 3) +/// Gt +/// +/// > compare(3, 3) +/// Eq +/// +pub fn compare(a: Int, with b: Int) -> Order { + case a == b { + True -> order.Eq + False -> + case a < b { + True -> order.Lt + False -> order.Gt + } } +} - /// Compares two int, returning the smaller of the two. - /// - /// ## Examples - /// - /// > min(2, 3) - /// 2 - /// - pub fn min(a: Int, b: Int) -> Int { - case a < b { - True -> a - False -> b - } +/// Compares two int, returning the smaller of the two. +/// +/// ## Examples +/// +/// > min(2, 3) +/// 2 +/// +pub fn min(a: Int, b: Int) -> Int { + case a < b { + True -> a + False -> b } +} - /// Compares two int, returning the larger of the two. - /// - /// ## Examples - /// - /// > max(2, 3) - /// 3 - /// - pub fn max(a: Int, b: Int) -> Int { - case a > b { - True -> a - False -> b - } +/// Compares two int, returning the larger of the two. +/// +/// ## Examples +/// +/// > max(2, 3) +/// 3 +/// +pub fn max(a: Int, b: Int) -> Int { + case a > b { + True -> a + False -> b } +} - /// Returns whether the value provided is even. - /// - /// ## Examples - /// - /// > is_even(2) - /// True - /// - /// > is_even(3) - /// False - /// - pub fn is_even(x: Int) -> Bool { - x % 2 == 0 - } +/// Returns whether the value provided is even. +/// +/// ## Examples +/// +/// > is_even(2) +/// True +/// +/// > is_even(3) +/// False +/// +pub fn is_even(x: Int) -> Bool { + x % 2 == 0 +} - /// Returns whether the value provided is odd. - /// - /// ## Examples - /// - /// > is_odd(3) - /// True - /// - /// > is_odd(2) - /// False - /// - pub fn is_odd(x: Int) -> Bool { - x % 2 != 0 - } +/// Returns whether the value provided is odd. +/// +/// ## Examples +/// +/// > is_odd(3) +/// True +/// +/// > is_odd(2) +/// False +/// +pub fn is_odd(x: Int) -> Bool { + x % 2 != 0 +} - /// Returns the negative of the value provided - /// - /// ## Examples - /// - /// > negate(1) - /// -1 - /// - pub fn negate(x: Int) -> Int { - -1 * x - } +/// Returns the negative of the value provided +/// +/// ## Examples +/// +/// > negate(1) +/// -1 +/// +pub fn negate(x: Int) -> Int { + -1 * x +} - /// Sums a list of Ints. - /// - /// ## Example - /// - /// > sum([1, 2, 3]) - /// 6 - /// - pub fn sum(numbers: List(Int)) -> Int { - numbers - |> do_sum(0) - } +/// Sums a list of Ints. +/// +/// ## Example +/// +/// > sum([1, 2, 3]) +/// 6 +/// +pub fn sum(numbers: List(Int)) -> Int { + numbers + |> do_sum(0) +} - fn do_sum(numbers: List(Int), initial: Int) -> Int { - case numbers { - [] -> initial - [x, ..rest] -> do_sum(rest, x + initial) - } +fn do_sum(numbers: List(Int), initial: Int) -> Int { + case numbers { + [] -> initial + [x, ..rest] -> do_sum(rest, x + initial) } +} - /// Multiplies a list of Ints and returns the product. - /// - /// ## Example - /// - /// > product([2, 3, 4]) - /// 24 - /// - pub fn product(numbers: List(Int)) -> Int { - case numbers { - [] -> 0 - _ -> do_product(numbers, 1) - } +/// Multiplies a list of Ints and returns the product. +/// +/// ## Example +/// +/// > product([2, 3, 4]) +/// 24 +/// +pub fn product(numbers: List(Int)) -> Int { + case numbers { + [] -> 0 + _ -> do_product(numbers, 1) } +} - fn do_product(numbers: List(Int), initial: Int) -> Int { - case numbers { - [] -> initial - [x, ..rest] -> do_product(rest, x * initial) - } +fn do_product(numbers: List(Int), initial: Int) -> Int { + case numbers { + [] -> initial + [x, ..rest] -> do_product(rest, x * initial) } } diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam index 33e32d1..682dd01 100644 --- a/src/gleam/list.gleam +++ b/src/gleam/list.gleam @@ -18,1560 +18,1616 @@ //// } //// -if erlang { - import gleam/int - import gleam/pair - import gleam/order.{Order} +import gleam/int +import gleam/pair +import gleam/order.{Order} - pub type List(elements) = - List(elements) +pub type List(elements) = + List(elements) - /// An error value returned by the `strict_zip` function. - /// - pub type LengthMismatch { - LengthMismatch - } +/// An error value returned by the `strict_zip` function. +/// +pub type LengthMismatch { + LengthMismatch +} - /// Counts the number of elements in a given list. - /// - /// This function has to traverse the list to determine the number of elements, - /// so it runs in linear time. - /// - /// This function is natively implemented by the virtual machine and is highly - /// optimised. - /// - /// ## Examples - /// - /// > length([]) - /// 0 - /// - /// > length([1]) - /// 1 - /// - /// > length([1, 2]) - /// 2 - /// - pub external fn length(of: List(a)) -> Int = - "erlang" "length" +/// Counts the number of elements in a given list. +/// +/// This function has to traverse the list to determine the number of elements, +/// so it runs in linear time. +/// +/// This function is natively implemented by the virtual machine and is highly +/// optimised. +/// +/// ## Examples +/// +/// > length([]) +/// 0 +/// +/// > length([1]) +/// 1 +/// +/// > length([1, 2]) +/// 2 +/// +pub fn length(of list: List(a)) -> Int { + do_length(list) +} - /// Creates a new list from a given list containing the same elements but in the - /// opposite order. - /// - /// This function has to traverse the list to create the new reversed list, so - /// it runs in linear time. - /// - /// This function is natively implemented by the virtual machine and is highly - /// optimised. - /// - /// ## Examples - /// - /// > reverse([]) - /// [] - /// - /// > reverse([1]) - /// [1] - /// - /// > reverse([1, 2]) - /// [2, 1] - /// - pub external fn reverse(List(a)) -> List(a) = - "lists" "reverse" +if erlang { + external fn do_length(List(a)) -> Int = + "erlang" "length" +} - /// Determines whether or not the list is empty. - /// - /// This function runs in constant time. - /// - /// ## Examples - /// - /// > is_empty([]) - /// True - /// - /// > is_empty([1]) - /// False - /// - /// > is_empty([1, 1]) - /// False - /// - pub fn is_empty(list: List(a)) -> Bool { - list == [] +if javascript { + fn do_length(list: List(a)) -> Int { + do_length_acc(list, 0) } - /// Determines whether or not a given element exists within a given list. - /// - /// This function traverses the list to find the element, so it runs in linear - /// time. - /// - /// ## Examples - /// - /// > [] |> contains(any: 0) - /// True - /// - /// > [0] |> contains(any: 0) - /// True - /// - /// > [1] |> contains(any: 0) - /// False - /// - /// > [1, 1] |> contains(any: 0) - /// False - /// - /// > [1, 0] |> contains(any: 0) - /// True - /// - pub fn contains(list: List(a), any elem: a) -> Bool { + fn do_length_acc(list: List(a), count: Int) -> Int { case list { - [] -> False - [head, ..rest] -> head == elem || contains(rest, elem) + [_, ..list] -> do_length_acc(list, count + 1) + _ -> count } } +} - /// Gets the first element from the start of the list, if there is one. - /// - /// ## Examples - /// - /// > head([]) - /// Error(Nil) - /// - /// > head([0]) - /// Ok(0) - /// - /// > head([1, 2]) - /// Ok(1) - /// - pub fn head(list: List(a)) -> Result(a, Nil) { - case list { - [] -> Error(Nil) - [x, .._] -> Ok(x) - } +/// Creates a new list from a given list containing the same elements but in the +/// opposite order. +/// +/// This function has to traverse the list to create the new reversed list, so +/// it runs in linear time. +/// +/// This function is natively implemented by the virtual machine and is highly +/// optimised. +/// +/// ## Examples +/// +/// > reverse([]) +/// [] +/// +/// > reverse([1]) +/// [1] +/// +/// > reverse([1, 2]) +/// [2, 1] +/// +pub fn reverse(xs: List(a)) -> List(a) { + do_reverse(xs) +} + +if erlang { + external fn do_reverse(List(a)) -> List(a) = + "lists" "reverse" +} + +if javascript { + pub fn do_reverse(list) { + do_reverse_acc(list, []) } - /// Gets the list minus the first element. If the list is empty `Error(Nil)` is - /// returned. - /// - /// This function runs in constant time and does not make a copy of the list. - /// - /// ## Examples - /// - /// > tail([]) - /// Error(Nil) - /// - /// > tail([0]) - /// Ok([]) - /// - /// > tail([1, 2]) - /// Ok([2]) - /// - pub fn tail(list: List(a)) -> Result(List(a), Nil) { - case list { - [] -> Error(Nil) - [_, ..xs] -> Ok(xs) + fn do_reverse_acc(remaining, accumulator) { + case remaining { + [] -> accumulator + [item, ..rest] -> do_reverse_acc(rest, [item, ..accumulator]) } } +} - fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { - case list { - [] -> reverse(acc) - [x, ..xs] -> { - let new_acc = case fun(x) { - True -> [x, ..acc] - False -> acc - } - do_filter(xs, fun, new_acc) - } - } +/// Determines whether or not the list is empty. +/// +/// This function runs in constant time. +/// +/// ## Examples +/// +/// > is_empty([]) +/// True +/// +/// > is_empty([1]) +/// False +/// +/// > is_empty([1, 1]) +/// False +/// +pub fn is_empty(list: List(a)) -> Bool { + list == [] +} + +/// Determines whether or not a given element exists within a given list. +/// +/// This function traverses the list to find the element, so it runs in linear +/// time. +/// +/// ## Examples +/// +/// > [] |> contains(any: 0) +/// True +/// +/// > [0] |> contains(any: 0) +/// True +/// +/// > [1] |> contains(any: 0) +/// False +/// +/// > [1, 1] |> contains(any: 0) +/// False +/// +/// > [1, 0] |> contains(any: 0) +/// True +/// +pub fn contains(list: List(a), any elem: a) -> Bool { + case list { + [] -> False + [head, ..rest] -> head == elem || contains(rest, elem) } +} - /// Returns a new list containing only the elements from the first list for - /// which the given functions returns `True`. - /// - /// ## Examples - /// - /// > filter([2, 4, 6, 1], fn(x) { x > 2 }) - /// [4, 6] - /// - /// > filter([2, 4, 6, 1], fn(x) { x > 6 }) - /// [] - /// - pub fn filter(list: List(a), for predicate: fn(a) -> Bool) -> List(a) { - do_filter(list, predicate, []) +/// Gets the first element from the start of the list, if there is one. +/// +/// ## Examples +/// +/// > head([]) +/// Error(Nil) +/// +/// > head([0]) +/// Ok(0) +/// +/// > head([1, 2]) +/// Ok(1) +/// +pub fn head(list: List(a)) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [x, .._] -> Ok(x) } +} - fn do_filter_map( - list: List(a), - fun: fn(a) -> Result(b, e), - acc: List(b), - ) -> List(b) { - case list { - [] -> reverse(acc) - [x, ..xs] -> { - let new_acc = case fun(x) { - Ok(x) -> [x, ..acc] - Error(_) -> acc - } - do_filter_map(xs, fun, new_acc) +/// Gets the list minus the first element. If the list is empty `Error(Nil)` is +/// returned. +/// +/// This function runs in constant time and does not make a copy of the list. +/// +/// ## Examples +/// +/// > tail([]) +/// Error(Nil) +/// +/// > tail([0]) +/// Ok([]) +/// +/// > tail([1, 2]) +/// Ok([2]) +/// +pub fn tail(list: List(a)) -> Result(List(a), Nil) { + case list { + [] -> Error(Nil) + [_, ..xs] -> Ok(xs) + } +} + +fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [x, ..xs] -> { + let new_acc = case fun(x) { + True -> [x, ..acc] + False -> acc } + do_filter(xs, fun, new_acc) } } +} - /// Returns a new list containing only the elements from the first list for - /// which the given functions returns `Ok(_)`. - /// - /// ## Examples - /// - /// > filter_map([2, 4, 6, 1], Error) - /// [] - /// - /// > filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) - /// [3, 4, 6, 2] - /// - pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) { - do_filter_map(list, fun, []) - } +/// Returns a new list containing only the elements from the first list for +/// which the given functions returns `True`. +/// +/// ## Examples +/// +/// > filter([2, 4, 6, 1], fn(x) { x > 2 }) +/// [4, 6] +/// +/// > filter([2, 4, 6, 1], fn(x) { x > 6 }) +/// [] +/// +pub fn filter(list: List(a), for predicate: fn(a) -> Bool) -> List(a) { + do_filter(list, predicate, []) +} - fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) { - case list { - [] -> reverse(acc) - [x, ..xs] -> do_map(xs, fun, [fun(x), ..acc]) +fn do_filter_map( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> List(b) { + case list { + [] -> reverse(acc) + [x, ..xs] -> { + let new_acc = case fun(x) { + Ok(x) -> [x, ..acc] + Error(_) -> acc + } + do_filter_map(xs, fun, new_acc) } } +} - /// Returns a new list containing only the elements of the first list after the - /// function has been applied to each one. - /// - /// ## Examples - /// - /// > map([2, 4, 6], fn(x) { x * 2 }) - /// [4, 8, 12] - /// - pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { - do_map(list, fun, []) - } +/// Returns a new list containing only the elements from the first list for +/// which the given functions returns `Ok(_)`. +/// +/// ## Examples +/// +/// > filter_map([2, 4, 6, 1], Error) +/// [] +/// +/// > filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) +/// [3, 4, 6, 2] +/// +pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) { + do_filter_map(list, fun, []) +} - /// Similar to map but also lets you pass around an accumulated value. - /// - /// ## Examples - /// - /// ``` - /// > map_fold( - /// over: [1, 2, 3], - /// from: 100, - /// with: fn(i, memo) { #(i * 2, memo + i) } - /// ) - /// #([2, 4, 6], 106) - /// ``` - /// - pub fn map_fold( - over list: List(a), - from memo: memo, - with fun: fn(a, memo) -> #(b, memo), - ) -> #(List(b), memo) { - fold( - over: list, - from: #([], memo), - with: fn(item, acc) { - let #(items, current_memo) = acc - let #(next_item, next_memo) = fun(item, current_memo) - #([next_item, ..items], next_memo) - }, - ) - |> pair.map_first(reverse) +fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) { + case list { + [] -> reverse(acc) + [x, ..xs] -> do_map(xs, fun, [fun(x), ..acc]) } +} - fn do_index_map( - list: List(a), - fun: fn(Int, a) -> b, - index: Int, - acc: List(b), - ) -> List(b) { - case list { - [] -> reverse(acc) - [x, ..xs] -> do_index_map(xs, fun, index + 1, [fun(index, x), ..acc]) - } - } +/// Returns a new list containing only the elements of the first list after the +/// function has been applied to each one. +/// +/// ## Examples +/// +/// > map([2, 4, 6], fn(x) { x * 2 }) +/// [4, 8, 12] +/// +pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { + do_map(list, fun, []) +} - /// Returns a new list containing only the elements of the first list after the - /// function has been applied to each one and their index. - /// - /// The index starts at 0, so the first element is 0, the second is 1, and so - /// on. - /// - /// ## Examples - /// - /// > index_map(["a", "b"], fn(i, x) { #(i, x) }) - /// [#(0, "a"), #(1, "b")] - /// - pub fn index_map(list: List(a), with fun: fn(Int, a) -> b) -> List(b) { - do_index_map(list, fun, 0, []) - } +/// Similar to map but also lets you pass around an accumulated value. +/// +/// ## Examples +/// +/// ``` +/// > map_fold( +/// over: [1, 2, 3], +/// from: 100, +/// with: fn(i, memo) { #(i * 2, memo + i) } +/// ) +/// #([2, 4, 6], 106) +/// ``` +/// +pub fn map_fold( + over list: List(a), + from memo: memo, + with fun: fn(a, memo) -> #(b, memo), +) -> #(List(b), memo) { + fold( + over: list, + from: #([], memo), + with: fn(item, acc) { + let #(items, current_memo) = acc + let #(next_item, next_memo) = fun(item, current_memo) + #([next_item, ..items], next_memo) + }, + ) + |> pair.map_first(reverse) +} - fn do_try_map( - list: List(a), - fun: fn(a) -> Result(b, e), - acc: List(b), - ) -> Result(List(b), e) { - case list { - [] -> Ok(reverse(acc)) - [x, ..xs] -> - case fun(x) { - Ok(y) -> do_try_map(xs, fun, [y, ..acc]) - Error(error) -> Error(error) - } +fn do_index_map( + list: List(a), + fun: fn(Int, a) -> b, + index: Int, + acc: List(b), +) -> List(b) { + case list { + [] -> reverse(acc) + [x, ..xs] -> { + let acc = [fun(index, x), ..acc] + do_index_map(xs, fun, index + 1, acc) } } +} - /// Takes a function that returns a Result applies it to each element in a - /// given list in tern. - /// - /// If the function returns `Ok(new_value)` for all elements in the list then a - /// list of the new values is returned. - /// - /// If the function returns `Error(reason)` for any of the elements then it is - /// returned immediately. None of the elements in the list are processed after - /// one returns an `Error`. - /// - /// ## Examples - /// - /// > try_map([1, 2, 3], fn(x) { Ok(x + 2) }) - /// Ok([3, 4, 5]) - /// - /// > try_map([1, 2, 3], fn(x) { Error(0) }) - /// Error(0) - /// - /// > try_map([[1], [2, 3]], head) - /// Ok([1, 2]) - /// - /// > try_map([[1], [], [2]], head) - /// Error(Nil) - /// - pub fn try_map( - over list: List(a), - with fun: fn(a) -> Result(b, e), - ) -> Result(List(b), e) { - do_try_map(list, fun, []) - } +/// Returns a new list containing only the elements of the first list after the +/// function has been applied to each one and their index. +/// +/// The index starts at 0, so the first element is 0, the second is 1, and so +/// on. +/// +/// ## Examples +/// +/// > index_map(["a", "b"], fn(i, x) { #(i, x) }) +/// [#(0, "a"), #(1, "b")] +/// +pub fn index_map(list: List(a), with fun: fn(Int, a) -> b) -> List(b) { + do_index_map(list, fun, 0, []) +} - /// Returns a list that is the given list with up to the given number of - /// elements removed from the front of the list. - /// - /// If the element has less than the number of elements an empty list is - /// returned. - /// - /// This function runs in linear time but does not copy the list. - /// - /// ## Examples - /// - /// > drop([1, 2, 3, 4], 2) - /// [3, 4] - /// - /// > drop([1, 2, 3, 4], 9) - /// [] - /// - pub fn drop(from list: List(a), up_to n: Int) -> List(a) { - case n <= 0 { - True -> list - False -> - case list { - [] -> [] - [_, ..xs] -> drop(xs, n - 1) - } - } +fn do_try_map( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> Result(List(b), e) { + case list { + [] -> Ok(reverse(acc)) + [x, ..xs] -> + case fun(x) { + Ok(y) -> do_try_map(xs, fun, [y, ..acc]) + Error(error) -> Error(error) + } } +} - fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) { - case n <= 0 { - True -> reverse(acc) - False -> - case list { - [] -> reverse(acc) - [x, ..xs] -> do_take(xs, n - 1, [x, ..acc]) - } - } - } +/// Takes a function that returns a Result applies it to each element in a +/// given list in tern. +/// +/// If the function returns `Ok(new_value)` for all elements in the list then a +/// list of the new values is returned. +/// +/// If the function returns `Error(reason)` for any of the elements then it is +/// returned immediately. None of the elements in the list are processed after +/// one returns an `Error`. +/// +/// ## Examples +/// +/// > try_map([1, 2, 3], fn(x) { Ok(x + 2) }) +/// Ok([3, 4, 5]) +/// +/// > try_map([1, 2, 3], fn(x) { Error(0) }) +/// Error(0) +/// +/// > try_map([[1], [2, 3]], head) +/// Ok([1, 2]) +/// +/// > try_map([[1], [], [2]], head) +/// Error(Nil) +/// +pub fn try_map( + over list: List(a), + with fun: fn(a) -> Result(b, e), +) -> Result(List(b), e) { + do_try_map(list, fun, []) +} - /// Returns a list containing the first given number of elements from the given - /// list. - /// - /// If the element has less than the number of elements then the full list is - /// returned. - /// - /// This function runs in linear time but does not copy the list. - /// - /// ## Examples - /// - /// > take([1, 2, 3, 4], 2) - /// [1, 2] - /// - /// > take([1, 2, 3, 4], 9) - /// [1, 2, 3, 4] - /// - pub fn take(from list: List(a), up_to n: Int) -> List(a) { - do_take(list, n, []) +/// Returns a list that is the given list with up to the given number of +/// elements removed from the front of the list. +/// +/// If the element has less than the number of elements an empty list is +/// returned. +/// +/// This function runs in linear time but does not copy the list. +/// +/// ## Examples +/// +/// > drop([1, 2, 3, 4], 2) +/// [3, 4] +/// +/// > drop([1, 2, 3, 4], 9) +/// [] +/// +pub fn drop(from list: List(a), up_to n: Int) -> List(a) { + case n <= 0 { + True -> list + False -> + case list { + [] -> [] + [_, ..xs] -> drop(xs, n - 1) + } } +} - /// Returns a new empty list. - /// - /// ## Examples - /// - /// > new() - /// [] - /// - pub fn new() -> List(a) { - [] +fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) { + case n <= 0 { + True -> reverse(acc) + False -> + case list { + [] -> reverse(acc) + [x, ..xs] -> do_take(xs, n - 1, [x, ..acc]) + } } +} - /// Joins one list onto the end of another. - /// - /// This function runs in linear time, and it traverses and copies the first - /// list. - /// - /// ## Examples - /// - /// > append([1, 2], [3]) - /// [1, 2, 3] - /// - pub external fn append(List(a), List(a)) -> List(a) = - "lists" "append" +/// Returns a list containing the first given number of elements from the given +/// list. +/// +/// If the element has less than the number of elements then the full list is +/// returned. +/// +/// This function runs in linear time but does not copy the list. +/// +/// ## Examples +/// +/// > take([1, 2, 3, 4], 2) +/// [1, 2] +/// +/// > take([1, 2, 3, 4], 9) +/// [1, 2, 3, 4] +/// +pub fn take(from list: List(a), up_to n: Int) -> List(a) { + do_take(list, n, []) +} - fn do_flatten(lists: List(List(a)), acc: List(a)) -> List(a) { - case lists { - [] -> acc - [l, ..rest] -> do_flatten(rest, append(acc, l)) - } - } +/// Returns a new empty list. +/// +/// ## Examples +/// +/// > new() +/// [] +/// +pub fn new() -> List(a) { + [] +} - /// Flattens a list of lists into a single list. - /// - /// This function runs in linear time, and it traverses and copies all the - /// inner lists. - /// - /// ## Examples - /// - /// > flatten([[1], [2, 3], []]) - /// [1, 2, 3] - /// - pub fn flatten(lists: List(List(a))) -> List(a) { - do_flatten(lists, []) - } +/// Joins one list onto the end of another. +/// +/// This function runs in linear time, and it traverses and copies the first +/// list. +/// +/// ## Examples +/// +/// > append([1, 2], [3]) +/// [1, 2, 3] +/// +pub fn append(first: List(a), second: List(a)) -> List(a) { + do_append(first, second) +} - /// Map and flatten the result - /// - /// ## Examples - /// - /// ``` - /// > flat_map([2, 4, 6], fn(x) { [x, x + 1] }) - /// [2, 3, 4, 5, 6, 7] - /// ``` - /// - pub fn flat_map(over list: List(a), with fun: fn(a) -> List(b)) -> List(b) { - map(list, fun) - |> flatten - } +if erlang { + external fn do_append(List(a), List(a)) -> List(a) = + "lists" "append" +} - /// Reduces a list of elements into a single value by calling a given function - /// on each element, going from left to right. - /// - /// `fold([1, 2, 3], 0, add)` is the equivalent of `add(3, add(2, add(1, 0)))`. - /// - /// This function runs in linear time. - /// - pub fn fold(over list: List(a), from initial: b, with fun: fn(a, b) -> b) -> b { - case list { - [] -> initial - [x, ..rest] -> fold(rest, fun(x, initial), fun) - } +if javascript { + fn do_append(first: List(a), second: List(a)) -> List(a) { + do_append_acc(reverse(first), second) } - /// Reduces a list of elements into a single value by calling a given function - /// on each element, going from right to left. - /// - /// `fold_right([1, 2, 3], 0, add)` is the equivalent of - /// `add(1, add(2, add(3, 0)))`. - /// - /// This function runs in linear time. - /// - /// Unlike `fold` this function is not tail recursive. Where possible use - /// `fold` instead as it will use less memory. - /// - pub fn fold_right( - over list: List(a), - from initial: b, - with fun: fn(a, b) -> b, - ) -> b { - case list { - [] -> initial - [x, ..rest] -> fun(x, fold_right(rest, initial, fun)) + fn do_append_acc(remaining: List(a), second: List(a)) -> List(a) { + case remaining { + [] -> second + [item, ..rest] -> do_append(rest, [item, ..second]) } } +} - fn do_index_fold( - over: List(a), - acc: b, - with: fn(Int, a, b) -> b, - index: Int, - ) -> b { - case over { - [] -> acc - [first, ..rest] -> - do_index_fold(rest, with(index, first, acc), with, index + 1) - } +fn do_flatten(lists: List(List(a)), acc: List(a)) -> List(a) { + case lists { + [] -> acc + [l, ..rest] -> do_flatten(rest, append(acc, l)) } +} - /// Like fold but the folding function also receives the index of the current element. - /// - /// ## Examples - /// - /// ``` - /// ["a", "b", "c"] - /// |> list.index_fold([], fn(index, item, acc) { ... }) - /// ``` - /// - pub fn index_fold( - over over: List(a), - from initial: b, - with fun: fn(Int, a, b) -> b, - ) -> b { - do_index_fold(over, initial, fun, 0) - } +/// Flattens a list of lists into a single list. +/// +/// This function runs in linear time, and it traverses and copies all the +/// inner lists. +/// +/// ## Examples +/// +/// > flatten([[1], [2, 3], []]) +/// [1, 2, 3] +/// +pub fn flatten(lists: List(List(a))) -> List(a) { + do_flatten(lists, []) +} - /// A variant of fold that might fail. - /// - /// The folding function should return `Result(accumulator, error) - /// If the returned value is `Ok(accumulator)` try_fold will try the next value in the list. - /// If the returned value is `Error(error)` try_fold will stop and return that error. - /// - /// ## Examples - /// - /// ``` - /// [1, 2, 3, 4] - /// |> try_fold(0, fn(i, acc) { - /// case i < 3 { - /// True -> Ok(acc + i) - /// False -> Error(Nil) - /// } - /// }) - /// ``` - /// - pub fn try_fold( - over collection: List(a), - from accumulator: b, - with fun: fn(a, b) -> Result(b, e), - ) -> Result(b, e) { - case collection { - [] -> Ok(accumulator) - [first, ..rest] -> - case fun(first, accumulator) { - Ok(next_accumulator) -> try_fold(rest, next_accumulator, fun) - Error(err) -> Error(err) - } - } - } +/// Map and flatten the result +/// +/// ## Examples +/// +/// ``` +/// > flat_map([2, 4, 6], fn(x) { [x, x + 1] }) +/// [2, 3, 4, 5, 6, 7] +/// ``` +/// +pub fn flat_map(over list: List(a), with fun: fn(a) -> List(b)) -> List(b) { + map(list, fun) + |> flatten +} - pub type ContinueOrStop(a) { - Continue(a) - Stop(a) +/// Reduces a list of elements into a single value by calling a given function +/// on each element, going from left to right. +/// +/// `fold([1, 2, 3], 0, add)` is the equivalent of `add(3, add(2, add(1, 0)))`. +/// +/// This function runs in linear time. +/// +pub fn fold(over list: List(a), from initial: b, with fun: fn(a, b) -> b) -> b { + case list { + [] -> initial + [x, ..rest] -> fold(rest, fun(x, initial), fun) } +} - /// A variant of fold that allows to stop folding earlier. - /// - /// The folding function should return `ContinueOrStop(accumulator) - /// If the returned value is `Continue(accumulator)` fold_until will try the next value in the list. - /// If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator. - /// - /// ## Examples - /// - /// ``` - /// [1, 2, 3, 4] - /// |> fold_until(0, fn(i, acc) { - /// case i < 3 { - /// True -> Continue(acc + i) - /// False -> Stop(acc) - /// } - /// }) - /// ``` - /// - pub fn fold_until( - over collection: List(a), - from accumulator: b, - with fun: fn(a, b) -> ContinueOrStop(b), - ) -> b { - case collection { - [] -> accumulator - [first, ..rest] -> - case fun(first, accumulator) { - Continue(next_accumulator) -> fold_until(rest, next_accumulator, fun) - Stop(b) -> b - } - } +/// Reduces a list of elements into a single value by calling a given function +/// on each element, going from right to left. +/// +/// `fold_right([1, 2, 3], 0, add)` is the equivalent of +/// `add(1, add(2, add(3, 0)))`. +/// +/// This function runs in linear time. +/// +/// Unlike `fold` this function is not tail recursive. Where possible use +/// `fold` instead as it will use less memory. +/// +pub fn fold_right( + over list: List(a), + from initial: b, + with fun: fn(a, b) -> b, +) -> b { + case list { + [] -> initial + [x, ..rest] -> fun(x, fold_right(rest, initial, fun)) } +} - /// Finds the first element in a given list for which the given function returns - /// True. - /// - /// Returns `Error(Nil)` if no the function does not return True for any of the - /// elements. - /// - /// ## Examples - /// - /// > find([1, 2, 3], fn(x) { x > 2 }) - /// Ok(3) - /// - /// > find([1, 2, 3], fn(x) { x > 4 }) - /// Error(Nil) - /// - /// > find([], fn(x) { True }) - /// Error(Nil) - /// - pub fn find( - in haystack: List(a), - one_that is_desired: fn(a) -> Bool, - ) -> Result(a, Nil) { - case haystack { - [] -> Error(Nil) - [x, ..rest] -> - case is_desired(x) { - True -> Ok(x) - _ -> find(in: rest, one_that: is_desired) - } - } +fn do_index_fold( + over: List(a), + acc: b, + with: fn(Int, a, b) -> b, + index: Int, +) -> b { + case over { + [] -> acc + [first, ..rest] -> + do_index_fold(rest, with(index, first, acc), with, index + 1) } +} - /// Finds the first element in a given list for which the given function returns - /// `Ok(new_value)` and return the new value for that element. - /// - /// Returns `Error(Nil)` if no the function does not return Ok for any of the - /// elements. - /// - /// ## Examples - /// - /// > find_map([[], [2], [3]], head) - /// Ok(2) - /// - /// > find_map([[], []], head) - /// Error(Nil) - /// - /// > find_map([], head) - /// Error(Nil) - /// - pub fn find_map( - in haystack: List(a), - with fun: fn(a) -> Result(b, c), - ) -> Result(b, Nil) { - case haystack { - [] -> Error(Nil) - [x, ..rest] -> - case fun(x) { - Ok(x) -> Ok(x) - _ -> find_map(in: rest, with: fun) - } - } - } +/// Like fold but the folding function also receives the index of the current element. +/// +/// ## Examples +/// +/// ``` +/// ["a", "b", "c"] +/// |> list.index_fold([], fn(index, item, acc) { ... }) +/// ``` +/// +pub fn index_fold( + over over: List(a), + from initial: b, + with fun: fn(Int, a, b) -> b, +) -> b { + do_index_fold(over, initial, fun, 0) +} - /// Returns True if the given function returns True for all the elements in - /// the given list. If the function returns False for any of the elements it - /// immediately returns False without checking the rest of the list. - /// - /// ## Examples - /// - /// > all([], fn(x) { x > 3 }) - /// True - /// - /// > all([4, 5], fn(x) { x > 3 }) - /// True - /// - /// > all([4, 3], fn(x) { x > 3 }) - /// False - /// - pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { - case list { - [] -> True - [x, ..rest] -> predicate(x) && all(rest, predicate) - } +/// A variant of fold that might fail. +/// +/// The folding function should return `Result(accumulator, error) +/// If the returned value is `Ok(accumulator)` try_fold will try the next value in the list. +/// If the returned value is `Error(error)` try_fold will stop and return that error. +/// +/// ## Examples +/// +/// ``` +/// [1, 2, 3, 4] +/// |> try_fold(0, fn(i, acc) { +/// case i < 3 { +/// True -> Ok(acc + i) +/// False -> Error(Nil) +/// } +/// }) +/// ``` +/// +pub fn try_fold( + over collection: List(a), + from accumulator: b, + with fun: fn(a, b) -> Result(b, e), +) -> Result(b, e) { + case collection { + [] -> Ok(accumulator) + [first, ..rest] -> + case fun(first, accumulator) { + Ok(next_accumulator) -> try_fold(rest, next_accumulator, fun) + Error(err) -> Error(err) + } } +} - /// Returns True if the given function returns True for any the elements in - /// the given list. If the function returns True for any of the elements it - /// immediately returns True without checking the rest of the list. - /// - /// ## Examples - /// - /// > any([], fn(x) { x > 3 }) - /// False - /// - /// > any([4, 5], fn(x) { x > 3 }) - /// False - /// - /// > any([4, 3], fn(x) { x > 3 }) - /// True - /// - /// > any([3, 4], fn(x) { x > 3 }) - /// True - /// - pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { - case list { - [] -> False - [x, ..rest] -> predicate(x) || any(rest, predicate) - } - } +pub type ContinueOrStop(a) { + Continue(a) + Stop(a) +} - fn do_zip(xs: List(a), ys: List(b), acc: List(#(a, b))) -> List(#(a, b)) { - case xs, ys { - [x, ..xs], [y, ..ys] -> do_zip(xs, ys, [#(x, y), ..acc]) - _, _ -> reverse(acc) - } +/// A variant of fold that allows to stop folding earlier. +/// +/// The folding function should return `ContinueOrStop(accumulator) +/// If the returned value is `Continue(accumulator)` fold_until will try the next value in the list. +/// If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator. +/// +/// ## Examples +/// +/// ``` +/// [1, 2, 3, 4] +/// |> fold_until(0, fn(i, acc) { +/// case i < 3 { +/// True -> Continue(acc + i) +/// False -> Stop(acc) +/// } +/// }) +/// ``` +/// +pub fn fold_until( + over collection: List(a), + from accumulator: b, + with fun: fn(a, b) -> ContinueOrStop(b), +) -> b { + case collection { + [] -> accumulator + [first, ..rest] -> + case fun(first, accumulator) { + Continue(next_accumulator) -> fold_until(rest, next_accumulator, fun) + Stop(b) -> b + } } +} - /// Takes two lists and returns a single list of 2 item tuples. - /// - /// If one of the lists is longer than the other the remaining elements from - /// the longer list are not used. - /// - /// ## Examples - /// - /// > zip([], []) - /// [] - /// - /// > zip([1, 2], [3]) - /// [#(1, 3)] - /// - /// > zip([1], [3, 4]) - /// [#(1, 3)] - /// - /// > zip([1, 2], [3, 4]) - /// [#(1, 3), #(2, 4)] - /// - pub fn zip(xs: List(a), ys: List(b)) -> List(#(a, b)) { - do_zip(xs, ys, []) +/// Finds the first element in a given list for which the given function returns +/// True. +/// +/// Returns `Error(Nil)` if no the function does not return True for any of the +/// elements. +/// +/// ## Examples +/// +/// > find([1, 2, 3], fn(x) { x > 2 }) +/// Ok(3) +/// +/// > find([1, 2, 3], fn(x) { x > 4 }) +/// Error(Nil) +/// +/// > find([], fn(x) { True }) +/// Error(Nil) +/// +pub fn find( + in haystack: List(a), + one_that is_desired: fn(a) -> Bool, +) -> Result(a, Nil) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case is_desired(x) { + True -> Ok(x) + _ -> find(in: rest, one_that: is_desired) + } } +} - /// Takes two lists and returns a single list of 2 item tuples. - /// - /// If one of the lists is longer than the other an Error is returned. - /// - /// ## Examples - /// - /// > strict_zip([], []) - /// Ok([]) - /// - /// > strict_zip([1, 2], [3]) - /// Error(LengthMismatch) - /// - /// > strict_zip([1], [3, 4]) - /// Error(LengthMismatch) - /// - /// > strict_zip([1, 2], [3, 4]) - /// Ok([#(1, 3), #(2, 4)]) - /// - pub fn strict_zip( - l1: List(a), - l2: List(b), - ) -> Result(List(#(a, b)), LengthMismatch) { - case length(of: l1) == length(of: l2) { - True -> Ok(zip(l1, l2)) - False -> Error(LengthMismatch) - } +/// Finds the first element in a given list for which the given function returns +/// `Ok(new_value)` and return the new value for that element. +/// +/// Returns `Error(Nil)` if no the function does not return Ok for any of the +/// elements. +/// +/// ## Examples +/// +/// > find_map([[], [2], [3]], head) +/// Ok(2) +/// +/// > find_map([[], []], head) +/// Error(Nil) +/// +/// > find_map([], head) +/// Error(Nil) +/// +pub fn find_map( + in haystack: List(a), + with fun: fn(a) -> Result(b, c), +) -> Result(b, Nil) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case fun(x) { + Ok(x) -> Ok(x) + _ -> find_map(in: rest, with: fun) + } } +} - fn do_unzip(input, xs, ys) { - case input { - [] -> #(reverse(xs), reverse(ys)) - [#(x, y), ..rest] -> do_unzip(rest, [x, ..xs], [y, ..ys]) - } +/// Returns True if the given function returns True for all the elements in +/// the given list. If the function returns False for any of the elements it +/// immediately returns False without checking the rest of the list. +/// +/// ## Examples +/// +/// > all([], fn(x) { x > 3 }) +/// True +/// +/// > all([4, 5], fn(x) { x > 3 }) +/// True +/// +/// > all([4, 3], fn(x) { x > 3 }) +/// False +/// +pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { + case list { + [] -> True + [x, ..rest] -> predicate(x) && all(rest, predicate) } +} - /// Takes a single list of 2 item tuples and returns two lists. - /// - /// ## Examples - /// - /// > unzip([#(1, 2), #(3, 4)]) - /// #([1, 3], [2, 4]) - /// - /// > unzip([]) - /// #([], []) - /// - pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b)) { - do_unzip(input, [], []) +/// Returns True if the given function returns True for any the elements in +/// the given list. If the function returns True for any of the elements it +/// immediately returns True without checking the rest of the list. +/// +/// ## Examples +/// +/// > any([], fn(x) { x > 3 }) +/// False +/// +/// > any([4, 5], fn(x) { x > 3 }) +/// False +/// +/// > any([4, 3], fn(x) { x > 3 }) +/// True +/// +/// > any([3, 4], fn(x) { x > 3 }) +/// True +/// +pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { + case list { + [] -> False + [x, ..rest] -> predicate(x) || any(rest, predicate) } +} - fn do_intersperse(list: List(a), separator: a, acc: List(a)) -> List(a) { - case list { - [] -> reverse(acc) - [x, ..rest] -> do_intersperse(rest, separator, [x, separator, ..acc]) - } +fn do_zip(xs: List(a), ys: List(b), acc: List(#(a, b))) -> List(#(a, b)) { + case xs, ys { + [x, ..xs], [y, ..ys] -> do_zip(xs, ys, [#(x, y), ..acc]) + _, _ -> reverse(acc) } +} - /// Inserts a given value between each existing element in a given list. - /// - /// This function runs in linear time and copies the list. - /// - /// ## Examples - /// - /// > intersperse([1, 1, 1], 2) - /// [1, 2, 1, 2, 1] - /// - /// > intersperse([], 2) - /// [] - /// - pub fn intersperse(list: List(a), with elem: a) -> List(a) { - case list { - [] | [_] -> list - [x, ..rest] -> do_intersperse(rest, elem, [x]) - } +/// Takes two lists and returns a single list of 2 item tuples. +/// +/// If one of the lists is longer than the other the remaining elements from +/// the longer list are not used. +/// +/// ## Examples +/// +/// > zip([], []) +/// [] +/// +/// > zip([1, 2], [3]) +/// [#(1, 3)] +/// +/// > zip([1], [3, 4]) +/// [#(1, 3)] +/// +/// > zip([1, 2], [3, 4]) +/// [#(1, 3), #(2, 4)] +/// +pub fn zip(xs: List(a), ys: List(b)) -> List(#(a, b)) { + do_zip(xs, ys, []) +} + +/// Takes two lists and returns a single list of 2 item tuples. +/// +/// If one of the lists is longer than the other an Error is returned. +/// +/// ## Examples +/// +/// > strict_zip([], []) +/// Ok([]) +/// +/// > strict_zip([1, 2], [3]) +/// Error(LengthMismatch) +/// +/// > strict_zip([1], [3, 4]) +/// Error(LengthMismatch) +/// +/// > strict_zip([1, 2], [3, 4]) +/// Ok([#(1, 3), #(2, 4)]) +/// +pub fn strict_zip( + l1: List(a), + l2: List(b), +) -> Result(List(#(a, b)), LengthMismatch) { + case length(of: l1) == length(of: l2) { + True -> Ok(zip(l1, l2)) + False -> Error(LengthMismatch) } +} - /// Returns the element in the Nth position in the list, with 0 being the first - /// position. - /// - /// Error(Nil) is returned if the list is not long enough for the given index. - /// - /// ## Examples - /// - /// > at([1, 2, 3], 1) - /// Ok(2) - /// - /// > at([1, 2, 3], 5) - /// Error(Nil) - /// - pub fn at(in list: List(a), get index: Int) -> Result(a, Nil) { - case index < 0 { - True -> Error(Nil) - False -> - case list { - [] -> Error(Nil) - [x, ..rest] -> - case index == 0 { - True -> Ok(x) - False -> at(rest, index - 1) - } - } - } +fn do_unzip(input, xs, ys) { + case input { + [] -> #(reverse(xs), reverse(ys)) + [#(x, y), ..rest] -> do_unzip(rest, [x, ..xs], [y, ..ys]) } +} - /// Removes any duplicate elements from a given list. - /// - /// This function returns in log-linear time (n log n). - /// - /// ## Examples - /// - /// > unique([1, 1, 1, 4, 7, 3, 3, 4]) - /// [1, 4, 7, 3] - /// - pub fn unique(list: List(a)) -> List(a) { - case list { - [] -> [] - [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))] - } +/// Takes a single list of 2 item tuples and returns two lists. +/// +/// ## Examples +/// +/// > unzip([#(1, 2), #(3, 4)]) +/// #([1, 3], [2, 4]) +/// +/// > unzip([]) +/// #([], []) +/// +pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b)) { + do_unzip(input, [], []) +} + +fn do_intersperse(list: List(a), separator: a, acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [x, ..rest] -> do_intersperse(rest, separator, [x, separator, ..acc]) } +} - fn merge_sort(a: List(a), b: List(a), compare: fn(a, a) -> Order) -> List(a) { - case a, b { - [], _ -> b - _, [] -> a - [ax, ..ar], [bx, ..br] -> - case compare(ax, bx) { - order.Lt -> [ax, ..merge_sort(ar, b, compare)] - _ -> [bx, ..merge_sort(a, br, compare)] - } - } +/// Inserts a given value between each existing element in a given list. +/// +/// This function runs in linear time and copies the list. +/// +/// ## Examples +/// +/// > intersperse([1, 1, 1], 2) +/// [1, 2, 1, 2, 1] +/// +/// > intersperse([], 2) +/// [] +/// +pub fn intersperse(list: List(a), with elem: a) -> List(a) { + case list { + [] | [_] -> list + [x, ..rest] -> do_intersperse(rest, elem, [x]) } +} - fn do_sort( - list: List(a), - compare: fn(a, a) -> Order, - list_length: Int, - ) -> List(a) { - case list_length < 2 { - True -> list - False -> { - let split_length = list_length / 2 - let a_list = take(list, split_length) - let b_list = drop(list, split_length) - merge_sort( - do_sort(a_list, compare, split_length), - do_sort(b_list, compare, list_length - split_length), - compare, - ) +/// Returns the element in the Nth position in the list, with 0 being the first +/// position. +/// +/// Error(Nil) is returned if the list is not long enough for the given index. +/// +/// ## Examples +/// +/// > at([1, 2, 3], 1) +/// Ok(2) +/// +/// > at([1, 2, 3], 5) +/// Error(Nil) +/// +pub fn at(in list: List(a), get index: Int) -> Result(a, Nil) { + case index < 0 { + True -> Error(Nil) + False -> + case list { + [] -> Error(Nil) + [x, ..rest] -> + case index == 0 { + True -> Ok(x) + False -> at(rest, index - 1) + } } - } } +} - /// Sorts from smallest to largest based upon the ordering specified by a given - /// function. - /// - /// ## Examples - /// - /// > import gleam/int - /// > list.sort([4, 3, 6, 5, 4, 1, 2], by: int.compare) - /// [1, 2, 3, 4, 4, 5, 6] - /// - pub fn sort(list: List(a), by compare: fn(a, a) -> Order) -> List(a) { - do_sort(list, compare, length(list)) +/// Removes any duplicate elements from a given list. +/// +/// This function returns in log-linear time (n log n). +/// +/// ## Examples +/// +/// > unique([1, 1, 1, 4, 7, 3, 3, 4]) +/// [1, 4, 7, 3] +/// +pub fn unique(list: List(a)) -> List(a) { + case list { + [] -> [] + [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))] } +} - /// Creates a list of ints ranging from a given start and finish. - /// - /// ## Examples - /// - /// > range(0, 0) - /// [] - /// - /// > range(0, 5) - /// [0, 1, 2, 3, 4] - /// - /// > range(1, -5) - /// [1, 0, -1, -2, -3, -4] - /// - pub fn range(from start: Int, to stop: Int) -> List(Int) { - case int.compare(start, stop) { - order.Eq -> [] - order.Gt -> [start, ..range(start - 1, stop)] - order.Lt -> [start, ..range(start + 1, stop)] - } +fn merge_sort(a: List(a), b: List(a), compare: fn(a, a) -> Order) -> List(a) { + case a, b { + [], _ -> b + _, [] -> a + [ax, ..ar], [bx, ..br] -> + case compare(ax, bx) { + order.Lt -> [ax, ..merge_sort(ar, b, compare)] + _ -> [bx, ..merge_sort(a, br, compare)] + } } +} - fn do_repeat(a: a, times: Int, acc: List(a)) -> List(a) { - case times <= 0 { - True -> acc - False -> do_repeat(a, times - 1, [a, ..acc]) +fn do_sort( + list: List(a), + compare: fn(a, a) -> Order, + list_length: Int, +) -> List(a) { + case list_length < 2 { + True -> list + False -> { + let split_length = list_length / 2 + let a_list = take(list, split_length) + let b_list = drop(list, split_length) + merge_sort( + do_sort(a_list, compare, split_length), + do_sort(b_list, compare, list_length - split_length), + compare, + ) } } +} - /// Builds a list of a given value a given number of times. - /// - /// ## Examples - /// - /// > repeat("a", times: 0) - /// [] - /// - /// > repeat("a", times: 5) - /// ["a", "a", "a", "a", "a"] - /// - pub fn repeat(item a: a, times times: Int) -> List(a) { - do_repeat(a, times, []) - } +/// Sorts from smallest to largest based upon the ordering specified by a given +/// function. +/// +/// ## Examples +/// +/// > import gleam/int +/// > list.sort([4, 3, 6, 5, 4, 1, 2], by: int.compare) +/// [1, 2, 3, 4, 4, 5, 6] +/// +pub fn sort(list: List(a), by compare: fn(a, a) -> Order) -> List(a) { + do_sort(list, compare, length(list)) +} - fn do_split(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) { - case n <= 0 { - True -> #(reverse(taken), list) - False -> - case list { - [] -> #(reverse(taken), []) - [x, ..xs] -> do_split(xs, n - 1, [x, ..taken]) - } - } +/// Creates a list of ints ranging from a given start and finish. +/// +/// ## Examples +/// +/// > range(0, 0) +/// [] +/// +/// > range(0, 5) +/// [0, 1, 2, 3, 4] +/// +/// > range(1, -5) +/// [1, 0, -1, -2, -3, -4] +/// +pub fn range(from start: Int, to stop: Int) -> List(Int) { + case int.compare(start, stop) { + order.Eq -> [] + order.Gt -> [start, ..range(start - 1, stop)] + order.Lt -> [start, ..range(start + 1, stop)] } +} - /// Splits a list in two before the given index. - /// - /// If the list is not long enough to have the given index the before list will - /// be the input list, and the after list will be empty. - /// - /// ## Examples - /// - /// > split([6, 7, 8, 9], 0) - /// #([], [6, 7, 8, 9]) - /// - /// > split([6, 7, 8, 9], 2) - /// #([6, 7], [8, 9]) - /// - /// > split([6, 7, 8, 9], 4) - /// #([6, 7, 8, 9], []) - /// - pub fn split(list list: List(a), at index: Int) -> #(List(a), List(a)) { - do_split(list, index, []) +fn do_repeat(a: a, times: Int, acc: List(a)) -> List(a) { + case times <= 0 { + True -> acc + False -> do_repeat(a, times - 1, [a, ..acc]) } +} - fn do_split_while( - list: List(a), - f: fn(a) -> Bool, - acc: List(a), - ) -> #(List(a), List(a)) { - case list { - [] -> #(reverse(acc), []) - [x, ..xs] -> - case f(x) { - False -> #(reverse(acc), list) - _ -> do_split_while(xs, f, [x, ..acc]) - } - } - } +/// Builds a list of a given value a given number of times. +/// +/// ## Examples +/// +/// > repeat("a", times: 0) +/// [] +/// +/// > repeat("a", times: 5) +/// ["a", "a", "a", "a", "a"] +/// +pub fn repeat(item a: a, times times: Int) -> List(a) { + do_repeat(a, times, []) +} - /// Splits a list in two before the first element that a given function returns - /// False for. - /// - /// If the function returns True for all elements the first list will be the - /// input list, and the second list will be empty. - /// - /// ## Examples - /// - /// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 }) - /// #([1, 2, 3], [4, 5]) - /// - /// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 }) - /// #([1, 2, 3, 4, 5], []) - /// - pub fn split_while( - list list: List(a), - satisfying predicate: fn(a) -> Bool, - ) -> #(List(a), List(a)) { - do_split_while(list, predicate, []) +fn do_split(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) { + case n <= 0 { + True -> #(reverse(taken), list) + False -> + case list { + [] -> #(reverse(taken), []) + [x, ..xs] -> do_split(xs, n - 1, [x, ..taken]) + } } +} - /// Given a list of 2 element tuples, finds the first tuple that has a given - /// key as the first element and returns the second element. - /// - /// If no tuple is found with the given key then `Error(Nil)` is returned. - /// - /// This function may be useful for interacting with Erlang code where lists of - /// tuples are common. - /// - /// ## Examples - /// - /// > key_find([#("a", 0), #("b", 1)], "a") - /// Ok(0) - /// - /// > key_find([#("a", 0), #("b", 1)], "b") - /// Ok(1) - /// - /// > key_find([#("a", 0), #("b", 1)], "c") - /// Error(Nil) - /// - pub fn key_find( - in keyword_list: List(#(k, v)), - find desired_key: k, - ) -> Result(v, Nil) { - find_map( - keyword_list, - fn(keyword) { - let #(key, value) = keyword - case key == desired_key { - True -> Ok(value) - False -> Error(Nil) - } - }, - ) - } +/// Splits a list in two before the given index. +/// +/// If the list is not long enough to have the given index the before list will +/// be the input list, and the after list will be empty. +/// +/// ## Examples +/// +/// > split([6, 7, 8, 9], 0) +/// #([], [6, 7, 8, 9]) +/// +/// > split([6, 7, 8, 9], 2) +/// #([6, 7], [8, 9]) +/// +/// > split([6, 7, 8, 9], 4) +/// #([6, 7, 8, 9], []) +/// +pub fn split(list list: List(a), at index: Int) -> #(List(a), List(a)) { + do_split(list, index, []) +} - fn do_pop(haystack, predicate, checked) { - case haystack { - [] -> Error(Nil) - [x, ..rest] -> - case predicate(x) { - True -> Ok(#(x, append(reverse(checked), rest))) - False -> do_pop(rest, predicate, [x, ..checked]) - } - } +fn do_split_while( + list: List(a), + f: fn(a) -> Bool, + acc: List(a), +) -> #(List(a), List(a)) { + case list { + [] -> #(reverse(acc), []) + [x, ..xs] -> + case f(x) { + False -> #(reverse(acc), list) + _ -> do_split_while(xs, f, [x, ..acc]) + } } +} - /// Removes the first element in a given list for which the predicate funtion returns `True`. - /// - /// Returns `Error(Nil)` if no the function does not return True for any of the - /// elements. - /// - /// ## Examples - /// - /// > pop([1, 2, 3], fn(x) { x > 2 }) - /// Ok(#(3, [1, 2])) - /// - /// > pop([1, 2, 3], fn(x) { x > 4 }) - /// Error(Nil) - /// - /// > pop([], fn(x) { True }) - /// Error(Nil) - /// - pub fn pop( - in haystack: List(a), - one_that is_desired: fn(a) -> Bool, - ) -> Result(#(a, List(a)), Nil) { - do_pop(haystack, is_desired, []) - } +/// Splits a list in two before the first element that a given function returns +/// False for. +/// +/// If the function returns True for all elements the first list will be the +/// input list, and the second list will be empty. +/// +/// ## Examples +/// +/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 }) +/// #([1, 2, 3], [4, 5]) +/// +/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 }) +/// #([1, 2, 3, 4, 5], []) +/// +pub fn split_while( + list list: List(a), + satisfying predicate: fn(a) -> Bool, +) -> #(List(a), List(a)) { + do_split_while(list, predicate, []) +} - fn do_pop_map(haystack, mapper, checked) { - case haystack { - [] -> Error(Nil) - [x, ..rest] -> - case mapper(x) { - Ok(y) -> Ok(#(y, append(reverse(checked), rest))) - Error(_) -> do_pop_map(rest, mapper, [x, ..checked]) - } - } - } +/// Given a list of 2 element tuples, finds the first tuple that has a given +/// key as the first element and returns the second element. +/// +/// If no tuple is found with the given key then `Error(Nil)` is returned. +/// +/// This function may be useful for interacting with Erlang code where lists of +/// tuples are common. +/// +/// ## Examples +/// +/// > key_find([#("a", 0), #("b", 1)], "a") +/// Ok(0) +/// +/// > key_find([#("a", 0), #("b", 1)], "b") +/// Ok(1) +/// +/// > key_find([#("a", 0), #("b", 1)], "c") +/// Error(Nil) +/// +pub fn key_find( + in keyword_list: List(#(k, v)), + find desired_key: k, +) -> Result(v, Nil) { + find_map( + keyword_list, + fn(keyword) { + let #(key, value) = keyword + case key == desired_key { + True -> Ok(value) + False -> Error(Nil) + } + }, + ) +} - /// Removes the first element in a given list for which the given function returns - /// `Ok(new_value)` and return the new value as well as list with the value removed. - /// - /// Returns `Error(Nil)` if no the function does not return Ok for any of the - /// elements. - /// - /// ## Examples - /// - /// > pop_map([[], [2], [3]], head) - /// Ok(#(2, [[], [3]])) - /// - /// > pop_map([[], []], head) - /// Error(Nil) - /// - /// > pop_map([], head) - /// Error(Nil) - /// - pub fn pop_map( - in haystack: List(a), - one_that is_desired: fn(a) -> Result(b, c), - ) -> Result(#(b, List(a)), Nil) { - do_pop_map(haystack, is_desired, []) +fn do_pop(haystack, predicate, checked) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case predicate(x) { + True -> Ok(#(x, append(reverse(checked), rest))) + False -> do_pop(rest, predicate, [x, ..checked]) + } } +} - /// Given a list of 2 element tuples, finds the first tuple that has a given - /// key as the first element. This function will return the second element - /// of the found tuple and list with tuple removed. - /// - /// If no tuple is found with the given key then `Error(Nil)` is returned. - /// - /// ## Examples - /// - /// > key_pop([#("a", 0), #("b", 1)], "a") - /// Ok(#(0, [#("b", 1)]) - /// - /// > key_pop([#("a", 0), #("b", 1)], "b") - /// Ok(#(1, [#("a", 0)]) - /// - /// > key_pop([#("a", 0), #("b", 1)], "c") - /// Error(Nil) - /// - pub fn key_pop( - haystack: List(#(k, v)), - key: k, - ) -> Result(#(v, List(#(k, v))), Nil) { - pop_map( - haystack, - fn(entry) { - let #(k, v) = entry - case k { - k if k == key -> Ok(v) - _ -> Error(Nil) - } - }, - ) - } +/// Removes the first element in a given list for which the predicate funtion returns `True`. +/// +/// Returns `Error(Nil)` if no the function does not return True for any of the +/// elements. +/// +/// ## Examples +/// +/// > pop([1, 2, 3], fn(x) { x > 2 }) +/// Ok(#(3, [1, 2])) +/// +/// > pop([1, 2, 3], fn(x) { x > 4 }) +/// Error(Nil) +/// +/// > pop([], fn(x) { True }) +/// Error(Nil) +/// +pub fn pop( + in haystack: List(a), + one_that is_desired: fn(a) -> Bool, +) -> Result(#(a, List(a)), Nil) { + do_pop(haystack, is_desired, []) +} - /// Given a list of 2 element tuples, inserts a key and value into the list. - /// - /// If there was already a tuple with the key then it is replaced, otherwise it - /// is added to the end of the list. - /// - /// - /// ## Examples - /// - /// > key_set([#(5, 0), #(4, 1)], 4, 100) - /// [#(5, 0), #(4, 100)] - /// - /// > key_set([#(5, 0), #(4, 1)], 1, 100) - /// [#(5, 0), #(4, 1), #(1, 100)] - /// - pub fn key_set(list: List(#(a, b)), key: a, value: b) -> List(#(a, b)) { - case list { - [] -> [#(key, value)] - [#(k, _), ..rest] if k == key -> [#(key, value), ..rest] - [first, ..rest] -> [first, ..key_set(rest, key, value)] - } +fn do_pop_map(haystack, mapper, checked) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case mapper(x) { + Ok(y) -> Ok(#(y, append(reverse(checked), rest))) + Error(_) -> do_pop_map(rest, mapper, [x, ..checked]) + } } +} - /// Calls a function for each element in a list, discarding the results. - /// - pub fn each(list: List(a), f: fn(a) -> b) -> Nil { - case list { - [] -> Nil - [x, ..xs] -> { - f(x) - each(xs, f) +/// Removes the first element in a given list for which the given function returns +/// `Ok(new_value)` and return the new value as well as list with the value removed. +/// +/// Returns `Error(Nil)` if no the function does not return Ok for any of the +/// elements. +/// +/// ## Examples +/// +/// > pop_map([[], [2], [3]], head) +/// Ok(#(2, [[], [3]])) +/// +/// > pop_map([[], []], head) +/// Error(Nil) +/// +/// > pop_map([], head) +/// Error(Nil) +/// +pub fn pop_map( + in haystack: List(a), + one_that is_desired: fn(a) -> Result(b, c), +) -> Result(#(b, List(a)), Nil) { + do_pop_map(haystack, is_desired, []) +} + +/// Given a list of 2 element tuples, finds the first tuple that has a given +/// key as the first element. This function will return the second element +/// of the found tuple and list with tuple removed. +/// +/// If no tuple is found with the given key then `Error(Nil)` is returned. +/// +/// ## Examples +/// +/// > key_pop([#("a", 0), #("b", 1)], "a") +/// Ok(#(0, [#("b", 1)]) +/// +/// > key_pop([#("a", 0), #("b", 1)], "b") +/// Ok(#(1, [#("a", 0)]) +/// +/// > key_pop([#("a", 0), #("b", 1)], "c") +/// Error(Nil) +/// +pub fn key_pop( + haystack: List(#(k, v)), + key: k, +) -> Result(#(v, List(#(k, v))), Nil) { + pop_map( + haystack, + fn(entry) { + let #(k, v) = entry + case k { + k if k == key -> Ok(v) + _ -> Error(Nil) } - } + }, + ) +} + +/// Given a list of 2 element tuples, inserts a key and value into the list. +/// +/// If there was already a tuple with the key then it is replaced, otherwise it +/// is added to the end of the list. +/// +/// +/// ## Examples +/// +/// > key_set([#(5, 0), #(4, 1)], 4, 100) +/// [#(5, 0), #(4, 100)] +/// +/// > key_set([#(5, 0), #(4, 1)], 1, 100) +/// [#(5, 0), #(4, 1), #(1, 100)] +/// +pub fn key_set(list: List(#(a, b)), key: a, value: b) -> List(#(a, b)) { + case list { + [] -> [#(key, value)] + [#(k, _), ..rest] if k == key -> [#(key, value), ..rest] + [first, ..rest] -> [first, ..key_set(rest, key, value)] } +} - fn do_partition(list, categorise, trues, falses) { - case list { - [] -> #(reverse(trues), reverse(falses)) - [x, ..xs] -> - case categorise(x) { - True -> do_partition(xs, categorise, [x, ..trues], falses) - False -> do_partition(xs, categorise, trues, [x, ..falses]) - } +/// Calls a function for each element in a list, discarding the results. +/// +pub fn each(list: List(a), f: fn(a) -> b) -> Nil { + case list { + [] -> Nil + [x, ..xs] -> { + f(x) + each(xs, f) } } +} - pub fn partition( - list: List(a), - with categorise: fn(a) -> Bool, - ) -> #(List(a), List(a)) { - do_partition(list, categorise, [], []) +fn do_partition(list, categorise, trues, falses) { + case list { + [] -> #(reverse(trues), reverse(falses)) + [x, ..xs] -> + case categorise(x) { + True -> do_partition(xs, categorise, [x, ..trues], falses) + False -> do_partition(xs, categorise, trues, [x, ..falses]) + } } +} - /// Returns all the permutations of a list - /// All values must be unique - /// - /// ## Examples - /// - /// > permutations([1, 2]) - /// [[1, 2], [2, 1]] - /// - pub fn permutations(l: List(a)) -> List(List(a)) { - case l { - [] -> [[]] - _ -> - map( - l, - fn(x) { - filter(l, fn(y) { y != x }) - |> permutations - |> map(append([x], _)) - }, - ) - |> flatten - } +pub fn partition( + list: List(a), + with categorise: fn(a) -> Bool, +) -> #(List(a), List(a)) { + do_partition(list, categorise, [], []) +} + +/// Returns all the permutations of a list +/// All values must be unique +/// +/// ## Examples +/// +/// > permutations([1, 2]) +/// [[1, 2], [2, 1]] +/// +pub fn permutations(l: List(a)) -> List(List(a)) { + case l { + [] -> [[]] + _ -> + map( + l, + fn(x) { + filter(l, fn(y) { y != x }) + |> permutations + |> map(append([x], _)) + }, + ) + |> flatten } +} - fn do_window(acc: List(List(a)), l: List(a), n: Int) -> List(List(a)) { - let window = take(l, n) +fn do_window(acc: List(List(a)), l: List(a), n: Int) -> List(List(a)) { + let window = take(l, n) - case length(window) == n { - True -> do_window([window, ..acc], drop(l, 1), n) - False -> acc - } + case length(window) == n { + True -> do_window([window, ..acc], drop(l, 1), n) + False -> acc } +} - /// Returns a list of sliding window - /// - /// ## Examples - /// - /// ``` - /// > window([1,2,3,4,5], 3) - /// [[1, 2, 3], [2, 3, 4], [3, 4, 5]] - /// - /// > window([1, 2], 4) - /// [] - /// ``` - /// - pub fn window(l: List(a), by n: Int) -> List(List(a)) { - do_window([], l, n) - |> reverse - } +/// Returns a list of sliding window +/// +/// ## Examples +/// +/// ``` +/// > window([1,2,3,4,5], 3) +/// [[1, 2, 3], [2, 3, 4], [3, 4, 5]] +/// +/// > window([1, 2], 4) +/// [] +/// ``` +/// +pub fn window(l: List(a), by n: Int) -> List(List(a)) { + do_window([], l, n) + |> reverse +} - /// Returns a list of tuples containing two contiguous elements - /// - /// ## Examples - /// - /// ``` - /// > window_by_2([1,2,3,4]) - /// [#(1, 2), #(2, 3), #(3, 4)] - /// - /// > window_by_2([1]) - /// [] - /// ``` - /// - pub fn window_by_2(l: List(a)) -> List(#(a, a)) { - zip(l, drop(l, 1)) - } +/// Returns a list of tuples containing two contiguous elements +/// +/// ## Examples +/// +/// ``` +/// > window_by_2([1,2,3,4]) +/// [#(1, 2), #(2, 3), #(3, 4)] +/// +/// > window_by_2([1]) +/// [] +/// ``` +/// +pub fn window_by_2(l: List(a)) -> List(#(a, a)) { + zip(l, drop(l, 1)) +} - /// Drops the first elements in a given list for which the predicate funtion returns `True`. - /// - /// ## Examples - /// - /// > drop_while([1, 2, 3, 4], fun (x) { x < 3 }) - /// [3, 4] - /// - pub fn drop_while( - in list: List(a), - satisfying predicate: fn(a) -> Bool, - ) -> List(a) { - case list { - [] -> [] - [x, ..xs] -> - case predicate(x) { - True -> drop_while(xs, predicate) - False -> [x, ..xs] - } - } +/// Drops the first elements in a given list for which the predicate funtion returns `True`. +/// +/// ## Examples +/// +/// > drop_while([1, 2, 3, 4], fun (x) { x < 3 }) +/// [3, 4] +/// +pub fn drop_while( + in list: List(a), + satisfying predicate: fn(a) -> Bool, +) -> List(a) { + case list { + [] -> [] + [x, ..xs] -> + case predicate(x) { + True -> drop_while(xs, predicate) + False -> [x, ..xs] + } } +} - fn do_take_while( - list: List(a), - predicate: fn(a) -> Bool, - acc: List(a), - ) -> List(a) { - case list { - [] -> reverse(acc) - [head, ..tail] -> - case predicate(head) { - True -> do_take_while(tail, predicate, [head, ..acc]) - False -> reverse(acc) - } - } +fn do_take_while( + list: List(a), + predicate: fn(a) -> Bool, + acc: List(a), +) -> List(a) { + case list { + [] -> reverse(acc) + [head, ..tail] -> + case predicate(head) { + True -> do_take_while(tail, predicate, [head, ..acc]) + False -> reverse(acc) + } } +} - /// Takes the first elements in a given list for which the predicate funtion returns `True`. - /// - /// ## Examples - /// - /// > take_while([1, 2, 3, 2, 4], fun (x) { x < 3 }) - /// [1, 2] - /// - pub fn take_while( - in list: List(a), - satisfying predicate: fn(a) -> Bool, - ) -> List(a) { - do_take_while(list, predicate, []) - } +/// Takes the first elements in a given list for which the predicate funtion returns `True`. +/// +/// ## Examples +/// +/// > take_while([1, 2, 3, 2, 4], fun (x) { x < 3 }) +/// [1, 2] +/// +pub fn take_while( + in list: List(a), + satisfying predicate: fn(a) -> Bool, +) -> List(a) { + do_take_while(list, predicate, []) +} - fn do_chunk( - list: List(a), - f: fn(a) -> key, - previous_key: key, - current_chunk: List(a), - acc: List(List(a)), - ) -> List(List(a)) { - case list { - [] -> reverse([reverse(current_chunk), ..acc]) - [head, ..tail] -> { - let key = f(head) - case key == previous_key { - False -> - do_chunk(tail, f, key, [head], [reverse(current_chunk), ..acc]) - True -> do_chunk(tail, f, key, [head, ..current_chunk], acc) - } +fn do_chunk( + list: List(a), + f: fn(a) -> key, + previous_key: key, + current_chunk: List(a), + acc: List(List(a)), +) -> List(List(a)) { + case list { + [] -> reverse([reverse(current_chunk), ..acc]) + [head, ..tail] -> { + let key = f(head) + case key == previous_key { + False -> do_chunk(tail, f, key, [head], [reverse(current_chunk), ..acc]) + True -> do_chunk(tail, f, key, [head, ..current_chunk], acc) } } } +} - /// Returns a list of chunks in which - /// the result of calling `f` on each element is the same. - /// - /// ## Examples - /// - /// > [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 }) - /// [[1], [2, 2], [3], [4, 4, 6], [7, 7]] - /// - pub fn chunk(in list: List(a), by f: fn(a) -> key) -> List(List(a)) { - case list { - [] -> [] - [head, ..tail] -> do_chunk(tail, f, f(head), [head], []) - } +/// Returns a list of chunks in which +/// the result of calling `f` on each element is the same. +/// +/// ## Examples +/// +/// > [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 }) +/// [[1], [2, 2], [3], [4, 4, 6], [7, 7]] +/// +pub fn chunk(in list: List(a), by f: fn(a) -> key) -> List(List(a)) { + case list { + [] -> [] + [head, ..tail] -> do_chunk(tail, f, f(head), [head], []) } +} - fn do_sized_chunk( - list: List(a), - count: Int, - left: Int, - current_chunk: List(a), - acc: List(List(a)), - ) -> List(List(a)) { - case list { - [] -> - case current_chunk { - [] -> reverse(acc) - remaining -> reverse([reverse(remaining), ..acc]) - } - [head, ..tail] -> { - let chunk = [head, ..current_chunk] - case left > 1 { - False -> - do_sized_chunk(tail, count, count, [], [reverse(chunk), ..acc]) - True -> do_sized_chunk(tail, count, left - 1, chunk, acc) - } +fn do_sized_chunk( + list: List(a), + count: Int, + left: Int, + current_chunk: List(a), + acc: List(List(a)), +) -> List(List(a)) { + case list { + [] -> + case current_chunk { + [] -> reverse(acc) + remaining -> reverse([reverse(remaining), ..acc]) + } + [head, ..tail] -> { + let chunk = [head, ..current_chunk] + case left > 1 { + False -> do_sized_chunk(tail, count, count, [], [reverse(chunk), ..acc]) + True -> do_sized_chunk(tail, count, left - 1, chunk, acc) } } } +} - /// Returns a list of chunks containing `count` elements each. - /// - /// If the last chunk does not have `count` elements, it is instead - /// a partial chunk, with less than `count` elements. - /// - /// For any `count` less than 1 this function behaves as if it was set to 1. - /// - /// ## Examples - /// - /// > [1, 2, 3, 4, 5, 6] |> chunk(into: 2) - /// [[1, 2], [3, 4], [5, 6]] - /// - /// > [1, 2, 3, 4, 5, 6, 7, 8] |> chunk(into: 3) - /// [[1, 2, 3], [4, 5, 6], [7, 8]] - /// - pub fn sized_chunk(in list: List(a), into count: Int) -> List(List(a)) { - do_sized_chunk(list, count, count, [], []) - } +/// Returns a list of chunks containing `count` elements each. +/// +/// If the last chunk does not have `count` elements, it is instead +/// a partial chunk, with less than `count` elements. +/// +/// For any `count` less than 1 this function behaves as if it was set to 1. +/// +/// ## Examples +/// +/// > [1, 2, 3, 4, 5, 6] |> chunk(into: 2) +/// [[1, 2], [3, 4], [5, 6]] +/// +/// > [1, 2, 3, 4, 5, 6, 7, 8] |> chunk(into: 3) +/// [[1, 2, 3], [4, 5, 6], [7, 8]] +/// +pub fn sized_chunk(in list: List(a), into count: Int) -> List(List(a)) { + do_sized_chunk(list, count, count, [], []) +} - /// This function acts similar to fold, but does not take an initial state. - /// Instead, it starts from the first element in the list - /// and combines it with each subsequent element in turn using the given function. - /// The function is called as fun(current_element, accumulator). - /// - /// Returns `Ok` to indicate a successful run, and `Error` if called on an empty list. - /// - /// ## Examples - /// - /// > [] |> reduce(fn(x, y) { x + y }) - /// Error(Nil) - /// - /// > [1, 2, 3, 4, 5] |> reduce(fn(x, y) { x + y }) - /// Ok(15) - /// - pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) { - case list { - [] -> Error(Nil) - [head, ..tail] -> - fold(tail, head, fun) - |> Ok - } +/// This function acts similar to fold, but does not take an initial state. +/// Instead, it starts from the first element in the list +/// and combines it with each subsequent element in turn using the given function. +/// The function is called as fun(current_element, accumulator). +/// +/// Returns `Ok` to indicate a successful run, and `Error` if called on an empty list. +/// +/// ## Examples +/// +/// > [] |> reduce(fn(x, y) { x + y }) +/// Error(Nil) +/// +/// > [1, 2, 3, 4, 5] |> reduce(fn(x, y) { x + y }) +/// Ok(15) +/// +pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [head, ..tail] -> + fold(tail, head, fun) + |> Ok } +} - fn do_scan( - list: List(a), - accumulator: b, - accumulated: List(b), - fun: fn(a, b) -> b, - ) -> List(b) { - case list { - [] -> reverse(accumulated) - [x, ..xs] -> { - let next = fun(x, accumulator) - do_scan(xs, next, [next, ..accumulated], fun) - } +fn do_scan( + list: List(a), + accumulator: b, + accumulated: List(b), + fun: fn(a, b) -> b, +) -> List(b) { + case list { + [] -> reverse(accumulated) + [x, ..xs] -> { + let next = fun(x, accumulator) + do_scan(xs, next, [next, ..accumulated], fun) } } +} - /// Similar to `fold`, but yields the state of the accumulator at each stage. - /// - /// ## Examples - /// - /// > scan(over: [1, 2, 3], from: 100, with: fn(i, acc) { acc + i }) - /// [101, 103, 106] - /// - pub fn scan( - over list: List(a), - from initial: b, - with fun: fn(a, b) -> b, - ) -> List(b) { - do_scan(list, initial, [], fun) - } +/// Similar to `fold`, but yields the state of the accumulator at each stage. +/// +/// ## Examples +/// +/// > scan(over: [1, 2, 3], from: 100, with: fn(i, acc) { acc + i }) +/// [101, 103, 106] +/// +pub fn scan( + over list: List(a), + from initial: b, + with fun: fn(a, b) -> b, +) -> List(b) { + do_scan(list, initial, [], fun) +} - /// Returns the last element in the given list. - /// - /// Returns `Error(Nil)` if the list is empty. - /// - /// This function runs in linear time. - /// For a collection oriented around performant access at either end, - /// see `gleam/queue.Queue`. - /// - /// ## Examples - /// - /// > last([]) - /// Error(Nil) - /// - /// > last([1, 2, 3, 4, 5]) - /// Ok(5) - /// - pub fn last(list: List(a)) -> Result(a, Nil) { - list - |> reduce(fn(elem, _) { elem }) - } +/// Returns the last element in the given list. +/// +/// Returns `Error(Nil)` if the list is empty. +/// +/// This function runs in linear time. +/// For a collection oriented around performant access at either end, +/// see `gleam/queue.Queue`. +/// +/// ## Examples +/// +/// > last([]) +/// Error(Nil) +/// +/// > last([1, 2, 3, 4, 5]) +/// Ok(5) +/// +pub fn last(list: List(a)) -> Result(a, Nil) { + list + |> reduce(fn(elem, _) { elem }) +} - /// Return unique combinations of elements in the list - /// - /// ## Examples - /// - /// ``` - /// > combinations([1, 2, 3], 2) - /// [[1, 2], [1, 3], [2, 3]] - /// - /// > combinations([1, 2, 3, 4], 3) - /// [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] - /// ``` - /// - pub fn combinations(items: List(a), by n: Int) -> List(List(a)) { - case n { - 0 -> [[]] - _ -> - case items { - [] -> [] - [x, ..xs] -> { - let first_combinations = - map(combinations(xs, n - 1), with: fn(com) { [x, ..com] }) - |> reverse - fold( - first_combinations, - combinations(xs, n), - fn(c, acc) { [c, ..acc] }, - ) - } +/// Return unique combinations of elements in the list +/// +/// ## Examples +/// +/// ``` +/// > combinations([1, 2, 3], 2) +/// [[1, 2], [1, 3], [2, 3]] +/// +/// > combinations([1, 2, 3, 4], 3) +/// [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] +/// ``` +/// +pub fn combinations(items: List(a), by n: Int) -> List(List(a)) { + case n { + 0 -> [[]] + _ -> + case items { + [] -> [] + [x, ..xs] -> { + let first_combinations = + map(combinations(xs, n - 1), with: fn(com) { [x, ..com] }) + |> reverse + fold( + first_combinations, + combinations(xs, n), + fn(c, acc) { [c, ..acc] }, + ) } - } + } } +} - fn do_combination_pairs(items: List(a)) -> List(List(#(a, a))) { - case items { - [] -> [] - [x, ..xs] -> { - let first_combinations = map(xs, with: fn(other) { #(x, other) }) - [first_combinations, ..do_combination_pairs(xs)] - } +fn do_combination_pairs(items: List(a)) -> List(List(#(a, a))) { + case items { + [] -> [] + [x, ..xs] -> { + let first_combinations = map(xs, with: fn(other) { #(x, other) }) + [first_combinations, ..do_combination_pairs(xs)] } } +} - /// Return unique pair combinations of elements in the list - /// - /// ## Examples - /// - /// ``` - /// > combination_pairs([1, 2, 3]) - /// [#(1, 2), #(1, 3), #(2, 3)] - /// ``` - /// - pub fn combination_pairs(items: List(a)) -> List(#(a, a)) { - do_combination_pairs(items) - |> flatten - } +/// Return unique pair combinations of elements in the list +/// +/// ## Examples +/// +/// ``` +/// > combination_pairs([1, 2, 3]) +/// [#(1, 2), #(1, 3), #(2, 3)] +/// ``` +/// +pub fn combination_pairs(items: List(a)) -> List(#(a, a)) { + do_combination_pairs(items) + |> flatten +} - /// Make a list alternating the elements from the given lists - /// - /// ## Examples - /// - /// ``` - /// > list.interleave([[1, 2], [101, 102], [201, 202]]) - /// [1, 101, 201, 2, 102, 202] - /// ``` - /// - pub fn interleave(list: List(List(a))) -> List(a) { - transpose(list) - |> flatten - } +/// Make a list alternating the elements from the given lists +/// +/// ## Examples +/// +/// ``` +/// > list.interleave([[1, 2], [101, 102], [201, 202]]) +/// [1, 101, 201, 2, 102, 202] +/// ``` +/// +pub fn interleave(list: List(List(a))) -> List(a) { + transpose(list) + |> flatten +} - /// Transpose rows and columns of the list of lists. - /// - /// ## Examples - /// - /// ``` - /// > transpose([[1, 2, 3], [101, 102, 103]]) - /// [[1, 101], [2, 102], [3, 103]] - /// ``` - pub fn transpose(list_of_list: List(List(a))) -> List(List(a)) { - let take_first = fn(list) { - case list { - [] -> [] - [f] -> [f] - [f, .._rest] -> [f] - } +/// Transpose rows and columns of the list of lists. +/// +/// ## Examples +/// +/// ``` +/// > transpose([[1, 2, 3], [101, 102, 103]]) +/// [[1, 101], [2, 102], [3, 103]] +/// ``` +pub fn transpose(list_of_list: List(List(a))) -> List(List(a)) { + let take_first = fn(list) { + case list { + [] -> [] + [f] -> [f] + [f, .._rest] -> [f] } + } - case list_of_list { - [] -> [] - [[], ..xss] -> transpose(xss) - rows -> { - let firsts = - rows - |> map(take_first) - |> flatten - let rest = transpose(map(rows, drop(_, 1))) - [firsts, ..rest] - } + case list_of_list { + [] -> [] + [[], ..xss] -> transpose(xss) + rows -> { + let firsts = + rows + |> map(take_first) + |> flatten + let rest = transpose(map(rows, drop(_, 1))) + [firsts, ..rest] } } } diff --git a/test/gleam/list_test.gleam b/test/gleam/list_test.gleam index bfe2c0b..ee33e88 100644 --- a/test/gleam/list_test.gleam +++ b/test/gleam/list_test.gleam @@ -1,726 +1,738 @@ -if erlang { - import gleam/should - import gleam/list - import gleam/int - import gleam/float - import gleam/string - import gleam/pair - - pub fn length_test() { - list.length([]) - |> should.equal(0) - - list.length([1]) - |> should.equal(1) - - list.length([1, 1]) - |> should.equal(2) - - list.length([1, 1, 1]) - |> should.equal(3) - } +import gleam/should +import gleam/list +import gleam/int +import gleam/float +import gleam/string +import gleam/pair - pub fn reverse_test() { - list.reverse([]) - |> should.equal([]) - list.reverse([1, 2, 3, 4, 5]) - |> should.equal([5, 4, 3, 2, 1]) - } +pub fn length_test() { + list.length([]) + |> should.equal(0) - pub fn is_empty_test() { - list.is_empty([]) - |> should.be_true - list.is_empty([1]) - |> should.be_false - } + list.length([1]) + |> should.equal(1) - pub fn contains_test() { - list.contains([0, 4, 5, 1], 1) - |> should.be_true - list.contains([0, 4, 5, 7], 1) - |> should.be_false - list.contains([], 1) - |> should.be_false - } + list.length([1, 1]) + |> should.equal(2) - pub fn head_test() { - list.head([0, 4, 5, 7]) - |> should.equal(Ok(0)) + list.length([1, 1, 1]) + |> should.equal(3) +} - list.head([]) - |> should.equal(Error(Nil)) - } +pub fn reverse_test() { + list.reverse([]) + |> should.equal([]) + list.reverse([1, 2, 3, 4, 5]) + |> should.equal([5, 4, 3, 2, 1]) +} - pub fn tail_test() { - list.tail([0, 4, 5, 7]) - |> should.equal(Ok([4, 5, 7])) +pub fn is_empty_test() { + list.is_empty([]) + |> should.be_true + list.is_empty([1]) + |> should.be_false +} - list.tail([0]) - |> should.equal(Ok([])) +pub fn contains_test() { + list.contains([0, 4, 5, 1], 1) + |> should.be_true + list.contains([0, 4, 5, 7], 1) + |> should.be_false + list.contains([], 1) + |> should.be_false +} - list.tail([]) - |> should.equal(Error(Nil)) - } +pub fn head_test() { + list.head([0, 4, 5, 7]) + |> should.equal(Ok(0)) - pub fn filter_test() { - [] - |> list.filter(fn(_) { True }) - |> should.equal([]) + list.head([]) + |> should.equal(Error(Nil)) +} - [0, 4, 5, 7, 3] - |> list.filter(fn(_) { True }) - |> should.equal([0, 4, 5, 7, 3]) +pub fn tail_test() { + list.tail([0, 4, 5, 7]) + |> should.equal(Ok([4, 5, 7])) - [0, 4, 5, 7, 3] - |> list.filter(fn(x) { x > 4 }) - |> should.equal([5, 7]) + list.tail([0]) + |> should.equal(Ok([])) - [0, 4, 5, 7, 3] - |> list.filter(fn(x) { x < 4 }) - |> should.equal([0, 3]) - } + list.tail([]) + |> should.equal(Error(Nil)) +} - pub fn filter_map_test() { - [2, 4, 6, 1] - |> list.filter_map(fn(x) { Ok(x + 1) }) - |> should.equal([3, 5, 7, 2]) +pub fn filter_test() { + [] + |> list.filter(fn(_) { True }) + |> should.equal([]) - [2, 4, 6, 1] - |> list.filter_map(Error) - |> should.equal([]) - } + [0, 4, 5, 7, 3] + |> list.filter(fn(_) { True }) + |> should.equal([0, 4, 5, 7, 3]) - pub fn map_test() { - [] - |> list.map(fn(x) { x * 2 }) - |> should.equal([]) + [0, 4, 5, 7, 3] + |> list.filter(fn(x) { x > 4 }) + |> should.equal([5, 7]) - [0, 4, 5, 7, 3] - |> list.map(fn(x) { x * 2 }) - |> should.equal([0, 8, 10, 14, 6]) - } + [0, 4, 5, 7, 3] + |> list.filter(fn(x) { x < 4 }) + |> should.equal([0, 3]) +} - pub fn map_fold_test() { - [1, 2, 3, 4] - |> list.map_fold(from: 0, with: fn(i, acc) { #(i * 2, acc + i) }) - |> should.equal(#([2, 4, 6, 8], 10)) - } +pub fn filter_map_test() { + [2, 4, 6, 1] + |> list.filter_map(fn(x) { Ok(x + 1) }) + |> should.equal([3, 5, 7, 2]) - pub fn try_map_test() { - let fun = fn(x) { - case x == 6 || x == 5 || x == 4 { - True -> Ok(x * 2) - False -> Error(x) - } - } + [2, 4, 6, 1] + |> list.filter_map(Error) + |> should.equal([]) +} - [5, 6, 5, 6] - |> list.try_map(fun) - |> should.equal(Ok([10, 12, 10, 12])) +pub fn map_test() { + [] + |> list.map(fn(x) { x * 2 }) + |> should.equal([]) - [4, 6, 5, 7, 3] - |> list.try_map(fun) - |> should.equal(Error(7)) - } + [0, 4, 5, 7, 3] + |> list.map(fn(x) { x * 2 }) + |> should.equal([0, 8, 10, 14, 6]) +} - pub fn drop_test() { - [] - |> list.drop(5) - |> should.equal([]) +pub fn map_fold_test() { + [1, 2, 3, 4] + |> list.map_fold(from: 0, with: fn(i, acc) { #(i * 2, acc + i) }) + |> should.equal(#([2, 4, 6, 8], 10)) +} - [1, 2, 3, 4, 5, 6, 7, 8] - |> list.drop(5) - |> should.equal([6, 7, 8]) +pub fn try_map_test() { + let fun = fn(x) { + case x == 6 || x == 5 || x == 4 { + True -> Ok(x * 2) + False -> Error(x) + } } - pub fn take_test() { - [] - |> list.take(5) - |> should.equal([]) - [1, 2, 3, 4, 5, 6, 7, 8] - |> list.take(5) - |> should.equal([1, 2, 3, 4, 5]) - } + [5, 6, 5, 6] + |> list.try_map(fun) + |> should.equal(Ok([10, 12, 10, 12])) - pub fn new_test() { - list.new() - |> should.equal([]) - } + [4, 6, 5, 7, 3] + |> list.try_map(fun) + |> should.equal(Error(7)) +} - pub fn append_test() { - list.append([1], [2, 3]) - |> should.equal([1, 2, 3]) - } +pub fn drop_test() { + [] + |> list.drop(5) + |> should.equal([]) - pub fn flatten_test() { - list.flatten([]) - |> should.equal([]) + [1, 2, 3, 4, 5, 6, 7, 8] + |> list.drop(5) + |> should.equal([6, 7, 8]) +} - list.flatten([[]]) - |> should.equal([]) +pub fn take_test() { + [] + |> list.take(5) + |> should.equal([]) + [1, 2, 3, 4, 5, 6, 7, 8] + |> list.take(5) + |> should.equal([1, 2, 3, 4, 5]) +} - list.flatten([[], [], []]) - |> should.equal([]) +pub fn new_test() { + list.new() + |> should.equal([]) +} - list.flatten([[1, 2], [], [3, 4]]) - |> should.equal([1, 2, 3, 4]) - } +pub fn append_test() { + list.append([1], [2, 3]) + |> should.equal([1, 2, 3]) +} - pub fn flat_map_test() { - list.flat_map([1, 10, 20], fn(x) { [x, x + 1] }) - |> should.equal([1, 2, 10, 11, 20, 21]) - } +pub fn flatten_test() { + list.flatten([]) + |> should.equal([]) - pub fn fold_test() { - [1, 2, 3] - |> list.fold([], fn(x, acc) { [x, ..acc] }) - |> should.equal([3, 2, 1]) - } + list.flatten([[]]) + |> should.equal([]) - pub fn fold_right_test() { - [1, 2, 3] - |> list.fold_right(from: [], with: fn(x, acc) { [x, ..acc] }) - |> should.equal([1, 2, 3]) - } + list.flatten([[], [], []]) + |> should.equal([]) - pub fn index_fold_test() { - ["a", "b", "c"] - |> list.index_fold([], fn(ix, i, acc) { [#(ix, i), ..acc] }) - |> should.equal([#(2, "c"), #(1, "b"), #(0, "a")]) - } + list.flatten([[1, 2], [], [3, 4]]) + |> should.equal([1, 2, 3, 4]) +} - pub fn fold_until_test() { - [1, 2, 3, 4] - |> list.fold_until( - from: 0, - with: fn(n, acc) { - case n < 4 { - True -> list.Continue(acc + n) - False -> list.Stop(acc) - } - }, - ) - |> should.equal(6) - } +pub fn flat_map_test() { + list.flat_map([1, 10, 20], fn(x) { [x, x + 1] }) + |> should.equal([1, 2, 10, 11, 20, 21]) +} - pub fn try_fold_test() { - [1, 2, 3] - |> list.try_fold( - 0, - fn(i, acc) { - case i < 4 { - True -> Ok(acc + i) - False -> Error(Nil) - } - }, - ) - |> should.equal(Ok(6)) - - [1, 2, 3] - |> list.try_fold( - 0, - fn(i, acc) { - case i < 3 { - True -> Ok(acc + i) - False -> Error(Nil) - } - }, - ) - |> should.equal(Error(Nil)) - } +pub fn fold_test() { + [1, 2, 3] + |> list.fold([], fn(x, acc) { [x, ..acc] }) + |> should.equal([3, 2, 1]) +} - pub fn find_map_test() { - let f = fn(x) { - case x { - 2 -> Ok(4) - _ -> Error(Nil) - } - } +pub fn fold_right_test() { + [1, 2, 3] + |> list.fold_right(from: [], with: fn(x, acc) { [x, ..acc] }) + |> should.equal([1, 2, 3]) +} + +pub fn index_fold_test() { + ["a", "b", "c"] + |> list.index_fold([], fn(ix, i, acc) { [#(ix, i), ..acc] }) + |> should.equal([#(2, "c"), #(1, "b"), #(0, "a")]) +} - [1, 2, 3] - |> list.find_map(with: f) - |> should.equal(Ok(4)) +pub fn fold_until_test() { + [1, 2, 3, 4] + |> list.fold_until( + from: 0, + with: fn(n, acc) { + case n < 4 { + True -> list.Continue(acc + n) + False -> list.Stop(acc) + } + }, + ) + |> should.equal(6) +} - [1, 3, 2] - |> list.find_map(with: f) - |> should.equal(Ok(4)) +pub fn try_fold_test() { + [1, 2, 3] + |> list.try_fold( + 0, + fn(i, acc) { + case i < 4 { + True -> Ok(acc + i) + False -> Error(Nil) + } + }, + ) + |> should.equal(Ok(6)) + + [1, 2, 3] + |> list.try_fold( + 0, + fn(i, acc) { + case i < 3 { + True -> Ok(acc + i) + False -> Error(Nil) + } + }, + ) + |> should.equal(Error(Nil)) +} - [1, 3] - |> list.find_map(with: f) - |> should.equal(Error(Nil)) +pub fn find_map_test() { + let f = fn(x) { + case x { + 2 -> Ok(4) + _ -> Error(Nil) + } } - pub fn find_test() { - let is_two = fn(x) { x == 2 } + [1, 2, 3] + |> list.find_map(with: f) + |> should.equal(Ok(4)) - [1, 2, 3] - |> list.find(one_that: is_two) - |> should.equal(Ok(2)) + [1, 3, 2] + |> list.find_map(with: f) + |> should.equal(Ok(4)) - [1, 3, 2] - |> list.find(one_that: is_two) - |> should.equal(Ok(2)) + [1, 3] + |> list.find_map(with: f) + |> should.equal(Error(Nil)) +} - [1, 3] - |> list.find(one_that: is_two) - |> should.equal(Error(Nil)) - } +pub fn find_test() { + let is_two = fn(x) { x == 2 } - pub fn all_test() { - list.all([1, 2, 3, 4, 5], fn(x) { x > 0 }) - |> should.equal(True) + [1, 2, 3] + |> list.find(one_that: is_two) + |> should.equal(Ok(2)) - list.all([1, 2, 3, 4, 5], fn(x) { x < 0 }) - |> should.equal(False) + [1, 3, 2] + |> list.find(one_that: is_two) + |> should.equal(Ok(2)) - list.all([], fn(_) { False }) - |> should.equal(True) - } + [1, 3] + |> list.find(one_that: is_two) + |> should.equal(Error(Nil)) +} - pub fn any_test() { - list.any([1, 2, 3, 4, 5], fn(x) { x == 2 }) - |> should.equal(True) +pub fn all_test() { + list.all([1, 2, 3, 4, 5], fn(x) { x > 0 }) + |> should.equal(True) - list.any([1, 2, 3, 4, 5], fn(x) { x < 0 }) - |> should.equal(False) + list.all([1, 2, 3, 4, 5], fn(x) { x < 0 }) + |> should.equal(False) - list.any([], fn(_) { False }) - |> should.equal(False) - } + list.all([], fn(_) { False }) + |> should.equal(True) +} - pub fn zip_test() { - list.zip([], [1, 2, 3]) - |> should.equal([]) +pub fn any_test() { + list.any([1, 2, 3, 4, 5], fn(x) { x == 2 }) + |> should.equal(True) - list.zip([1, 2], []) - |> should.equal([]) + list.any([1, 2, 3, 4, 5], fn(x) { x < 0 }) + |> should.equal(False) - list.zip([1, 2, 3], [4, 5, 6]) - |> should.equal([#(1, 4), #(2, 5), #(3, 6)]) + list.any([], fn(_) { False }) + |> should.equal(False) +} - list.zip([5, 6], [1, 2, 3]) - |> should.equal([#(5, 1), #(6, 2)]) +pub fn zip_test() { + list.zip([], [1, 2, 3]) + |> should.equal([]) - list.zip([5, 6, 7], [1, 2]) - |> should.equal([#(5, 1), #(6, 2)]) - } + list.zip([1, 2], []) + |> should.equal([]) - pub fn strict_zip_test() { - list.strict_zip([], [1, 2, 3]) - |> should.equal(Error(list.LengthMismatch)) + list.zip([1, 2, 3], [4, 5, 6]) + |> should.equal([#(1, 4), #(2, 5), #(3, 6)]) - list.strict_zip([1, 2], []) - |> should.equal(Error(list.LengthMismatch)) + list.zip([5, 6], [1, 2, 3]) + |> should.equal([#(5, 1), #(6, 2)]) - list.strict_zip([1, 2, 3], [4, 5, 6]) - |> should.equal(Ok([#(1, 4), #(2, 5), #(3, 6)])) + list.zip([5, 6, 7], [1, 2]) + |> should.equal([#(5, 1), #(6, 2)]) +} - list.strict_zip([5, 6], [1, 2, 3]) - |> should.equal(Error(list.LengthMismatch)) +pub fn strict_zip_test() { + list.strict_zip([], [1, 2, 3]) + |> should.equal(Error(list.LengthMismatch)) - list.strict_zip([5, 6, 7], [1, 2]) - |> should.equal(Error(list.LengthMismatch)) - } + list.strict_zip([1, 2], []) + |> should.equal(Error(list.LengthMismatch)) - pub fn unzip_test() { - list.unzip([#(1, 2), #(3, 4)]) - |> should.equal(#([1, 3], [2, 4])) + list.strict_zip([1, 2, 3], [4, 5, 6]) + |> should.equal(Ok([#(1, 4), #(2, 5), #(3, 6)])) - list.unzip([]) - |> should.equal(#([], [])) - } + list.strict_zip([5, 6], [1, 2, 3]) + |> should.equal(Error(list.LengthMismatch)) - pub fn intersperse_test() { - list.intersperse([1, 2, 3], 4) - |> should.equal([1, 4, 2, 4, 3]) + list.strict_zip([5, 6, 7], [1, 2]) + |> should.equal(Error(list.LengthMismatch)) +} - list.intersperse([], 2) - |> should.equal([]) - } +pub fn unzip_test() { + list.unzip([#(1, 2), #(3, 4)]) + |> should.equal(#([1, 3], [2, 4])) + + list.unzip([]) + |> should.equal(#([], [])) +} - pub fn at_test() { - list.at([1, 2, 3], 2) - |> should.equal(Ok(3)) +pub fn intersperse_test() { + list.intersperse([1, 2, 3], 4) + |> should.equal([1, 4, 2, 4, 3]) - list.at([1, 2, 3], 5) - |> should.equal(Error(Nil)) + list.intersperse([], 2) + |> should.equal([]) +} - list.at([], 0) - |> should.equal(Error(Nil)) +pub fn at_test() { + list.at([1, 2, 3], 2) + |> should.equal(Ok(3)) - list.at([1, 2, 3, 4, 5, 6], -1) - |> should.equal(Error(Nil)) - } + list.at([1, 2, 3], 5) + |> should.equal(Error(Nil)) - pub fn unique_test() { - list.unique([1, 1, 2, 3, 4, 4, 4, 5, 6]) - |> should.equal([1, 2, 3, 4, 5, 6]) + list.at([], 0) + |> should.equal(Error(Nil)) - list.unique([7, 1, 45, 6, 2, 47, 2, 7, 5]) - |> should.equal([7, 1, 45, 6, 2, 47, 5]) + list.at([1, 2, 3, 4, 5, 6], -1) + |> should.equal(Error(Nil)) +} - list.unique([3, 4, 5]) - |> should.equal([3, 4, 5]) +pub fn unique_test() { + list.unique([1, 1, 2, 3, 4, 4, 4, 5, 6]) + |> should.equal([1, 2, 3, 4, 5, 6]) - list.unique([]) - |> should.equal([]) - } + list.unique([7, 1, 45, 6, 2, 47, 2, 7, 5]) + |> should.equal([7, 1, 45, 6, 2, 47, 5]) - pub fn sort_test() { - [4, 3, 6, 5, 4] - |> list.sort(int.compare) - |> should.equal([3, 4, 4, 5, 6]) + list.unique([3, 4, 5]) + |> should.equal([3, 4, 5]) - [4, 3, 6, 5, 4, 1] - |> list.sort(int.compare) - |> should.equal([1, 3, 4, 4, 5, 6]) + list.unique([]) + |> should.equal([]) +} - [4.1, 3.1, 6.1, 5.1, 4.1] - |> list.sort(float.compare) - |> should.equal([3.1, 4.1, 4.1, 5.1, 6.1]) +pub fn sort_test() { + [4, 3, 6, 5, 4] + |> list.sort(int.compare) + |> should.equal([3, 4, 4, 5, 6]) - [] - |> list.sort(int.compare) - |> should.equal([]) - } + [4, 3, 6, 5, 4, 1] + |> list.sort(int.compare) + |> should.equal([1, 3, 4, 4, 5, 6]) - pub fn index_map_test() { - list.index_map([3, 4, 5], fn(i, x) { #(i, x) }) - |> should.equal([#(0, 3), #(1, 4), #(2, 5)]) + [4.1, 3.1, 6.1, 5.1, 4.1] + |> list.sort(float.compare) + |> should.equal([3.1, 4.1, 4.1, 5.1, 6.1]) - let f = fn(i, x) { string.append(x, int.to_string(i)) } - list.index_map(["a", "b", "c"], f) - |> should.equal(["a0", "b1", "c2"]) - } + [] + |> list.sort(int.compare) + |> should.equal([]) +} - pub fn range_test() { - list.range(0, 0) - |> should.equal([]) +pub fn index_map_test() { + list.index_map([3, 4, 5], fn(i, x) { #(i, x) }) + |> should.equal([#(0, 3), #(1, 4), #(2, 5)]) - list.range(1, 1) - |> should.equal([]) + let f = fn(i, x) { #(x, i) } + list.index_map(["a", "b"], f) + |> should.equal([#("a", 0), #("b", 1)]) - list.range(-1, -1) - |> should.equal([]) + let f = fn(i, x) { #(x, i) } + list.index_map(["a", "b", "c"], f) + |> should.equal([#("a", 0), #("b", 1), #("c", 2)]) +} - list.range(0, 1) - |> should.equal([0]) +pub fn range_test() { + list.range(0, 0) + |> should.equal([]) - list.range(0, 5) - |> should.equal([0, 1, 2, 3, 4]) + list.range(1, 1) + |> should.equal([]) - list.range(1, -5) - |> should.equal([1, 0, -1, -2, -3, -4]) - } + list.range(-1, -1) + |> should.equal([]) - pub fn repeat_test() { - list.repeat(1, -10) - |> should.equal([]) + list.range(0, 1) + |> should.equal([0]) - list.repeat(1, 0) - |> should.equal([]) + list.range(0, 5) + |> should.equal([0, 1, 2, 3, 4]) - list.repeat(2, 3) - |> should.equal([2, 2, 2]) + list.range(1, -5) + |> should.equal([1, 0, -1, -2, -3, -4]) +} - list.repeat("x", 5) - |> should.equal(["x", "x", "x", "x", "x"]) - } +pub fn repeat_test() { + list.repeat(1, -10) + |> should.equal([]) - pub fn split_test() { - [] - |> list.split(0) - |> should.equal(#([], [])) + list.repeat(1, 0) + |> should.equal([]) - [0, 1, 2, 3, 4] - |> list.split(0) - |> should.equal(#([], [0, 1, 2, 3, 4])) + list.repeat(2, 3) + |> should.equal([2, 2, 2]) - [0, 1, 2, 3, 4] - |> list.split(-2) - |> should.equal(#([], [0, 1, 2, 3, 4])) + list.repeat("x", 5) + |> should.equal(["x", "x", "x", "x", "x"]) +} - [0, 1, 2, 3, 4] - |> list.split(1) - |> should.equal(#([0], [1, 2, 3, 4])) +pub fn split_test() { + [] + |> list.split(0) + |> should.equal(#([], [])) - [0, 1, 2, 3, 4] - |> list.split(3) - |> should.equal(#([0, 1, 2], [3, 4])) + [0, 1, 2, 3, 4] + |> list.split(0) + |> should.equal(#([], [0, 1, 2, 3, 4])) - [0, 1, 2, 3, 4] - |> list.split(9) - |> should.equal(#([0, 1, 2, 3, 4], [])) - } + [0, 1, 2, 3, 4] + |> list.split(-2) + |> should.equal(#([], [0, 1, 2, 3, 4])) - pub fn split_while_test() { - [] - |> list.split_while(fn(x) { x <= 5 }) - |> should.equal(#([], [])) + [0, 1, 2, 3, 4] + |> list.split(1) + |> should.equal(#([0], [1, 2, 3, 4])) - [1, 2, 3, 4, 5] - |> list.split_while(fn(x) { x <= 5 }) - |> should.equal(#([1, 2, 3, 4, 5], [])) + [0, 1, 2, 3, 4] + |> list.split(3) + |> should.equal(#([0, 1, 2], [3, 4])) - [1, 2, 3, 4, 5] - |> list.split_while(fn(x) { x == 2 }) - |> should.equal(#([], [1, 2, 3, 4, 5])) + [0, 1, 2, 3, 4] + |> list.split(9) + |> should.equal(#([0, 1, 2, 3, 4], [])) +} - [1, 2, 3, 4, 5] - |> list.split_while(fn(x) { x <= 3 }) - |> should.equal(#([1, 2, 3], [4, 5])) +pub fn split_while_test() { + [] + |> list.split_while(fn(x) { x <= 5 }) + |> should.equal(#([], [])) - [1, 2, 3, 4, 5] - |> list.split_while(fn(x) { x <= -3 }) - |> should.equal(#([], [1, 2, 3, 4, 5])) - } + [1, 2, 3, 4, 5] + |> list.split_while(fn(x) { x <= 5 }) + |> should.equal(#([1, 2, 3, 4, 5], [])) - pub fn key_find_test() { - let proplist = [#(0, "1"), #(1, "2")] + [1, 2, 3, 4, 5] + |> list.split_while(fn(x) { x == 2 }) + |> should.equal(#([], [1, 2, 3, 4, 5])) - proplist - |> list.key_find(0) - |> should.equal(Ok("1")) + [1, 2, 3, 4, 5] + |> list.split_while(fn(x) { x <= 3 }) + |> should.equal(#([1, 2, 3], [4, 5])) - proplist - |> list.key_find(1) - |> should.equal(Ok("2")) + [1, 2, 3, 4, 5] + |> list.split_while(fn(x) { x <= -3 }) + |> should.equal(#([], [1, 2, 3, 4, 5])) +} - proplist - |> list.key_find(2) - |> should.equal(Error(Nil)) - } +pub fn key_find_test() { + let proplist = [#(0, "1"), #(1, "2")] - pub fn pop_test() { - list.pop([1, 2, 3], fn(x) { x > 2 }) - |> should.equal(Ok(#(3, [1, 2]))) + proplist + |> list.key_find(0) + |> should.equal(Ok("1")) - list.pop([1, 2, 3], fn(x) { x > 4 }) - |> should.equal(Error(Nil)) + proplist + |> list.key_find(1) + |> should.equal(Ok("2")) - list.pop([], fn(_x) { True }) - |> should.equal(Error(Nil)) - } + proplist + |> list.key_find(2) + |> should.equal(Error(Nil)) +} - pub fn pop_map_test() { - list.pop_map(["foo", "2", "3"], int.parse) - |> should.equal(Ok(#(2, ["foo", "3"]))) +pub fn pop_test() { + list.pop([1, 2, 3], fn(x) { x > 2 }) + |> should.equal(Ok(#(3, [1, 2]))) - list.pop_map(["foo", "bar"], int.parse) - |> should.equal(Error(Nil)) + list.pop([1, 2, 3], fn(x) { x > 4 }) + |> should.equal(Error(Nil)) - list.pop_map([], int.parse) - |> should.equal(Error(Nil)) + list.pop([], fn(_x) { True }) + |> should.equal(Error(Nil)) +} + +pub fn pop_map_test() { + let get = fn(x) { + case x > 0 { + True -> Ok(x * 2) + False -> Error(Nil) + } } + list.pop_map([0, 2, 3], get) + |> should.equal(Ok(#(4, [0, 3]))) - pub fn key_pop_test() { - list.key_pop([#("a", 0), #("b", 1)], "a") - |> should.equal(Ok(#(0, [#("b", 1)]))) + list.pop_map([0, -1], get) + |> should.equal(Error(Nil)) - list.key_pop([#("a", 0), #("b", 1)], "b") - |> should.equal(Ok(#(1, [#("a", 0)]))) + list.pop_map([], get) + |> should.equal(Error(Nil)) +} - list.key_pop([#("a", 0), #("b", 1)], "c") - |> should.equal(Error(Nil)) - } +pub fn key_pop_test() { + list.key_pop([#("a", 0), #("b", 1)], "a") + |> should.equal(Ok(#(0, [#("b", 1)]))) - pub fn key_set_test() { - [#(5, 0), #(4, 1)] - |> list.key_set(4, 100) - |> should.equal([#(5, 0), #(4, 100)]) + list.key_pop([#("a", 0), #("b", 1)], "b") + |> should.equal(Ok(#(1, [#("a", 0)]))) - [#(5, 0), #(4, 1)] - |> list.key_set(1, 100) - |> should.equal([#(5, 0), #(4, 1), #(1, 100)]) - } + list.key_pop([#("a", 0), #("b", 1)], "c") + |> should.equal(Error(Nil)) +} - pub fn partition_test() { - [1, 2, 3, 4, 5, 6, 7] - |> list.partition(int.is_odd) - |> should.equal(#([1, 3, 5, 7], [2, 4, 6])) - } +pub fn key_set_test() { + [#(5, 0), #(4, 1)] + |> list.key_set(4, 100) + |> should.equal([#(5, 0), #(4, 100)]) - pub fn permutations_test() { - [1, 2] - |> list.permutations - |> should.equal([[1, 2], [2, 1]]) - - let expected = [ - [1, 2, 3], - [1, 3, 2], - [2, 1, 3], - [2, 3, 1], - [3, 1, 2], - [3, 2, 1], - ] - - [1, 2, 3] - |> list.permutations - |> should.equal(expected) - - ["a", "b"] - |> list.permutations - |> should.equal([["a", "b"], ["b", "a"]]) - } + [#(5, 0), #(4, 1)] + |> list.key_set(1, 100) + |> should.equal([#(5, 0), #(4, 1), #(1, 100)]) +} - pub fn window_test() { - [1, 2, 3] - |> list.window(by: 2) - |> should.equal([[1, 2], [2, 3]]) +pub fn partition_test() { + [1, 2, 3, 4, 5, 6, 7] + |> list.partition(int.is_odd) + |> should.equal(#([1, 3, 5, 7], [2, 4, 6])) +} - [1, 2, 3] - |> list.window(3) - |> should.equal([[1, 2, 3]]) +pub fn permutations_test() { + [1, 2] + |> list.permutations + |> should.equal([[1, 2], [2, 1]]) + + let expected = [ + [1, 2, 3], + [1, 3, 2], + [2, 1, 3], + [2, 3, 1], + [3, 1, 2], + [3, 2, 1], + ] + + [1, 2, 3] + |> list.permutations + |> should.equal(expected) + + ["a", "b"] + |> list.permutations + |> should.equal([["a", "b"], ["b", "a"]]) +} - [1, 2, 3] - |> list.window(4) - |> should.equal([]) +pub fn window_test() { + [1, 2, 3] + |> list.window(by: 2) + |> should.equal([[1, 2], [2, 3]]) - [1, 2, 3, 4, 5] - |> list.window(3) - |> should.equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]]) - } + [1, 2, 3] + |> list.window(3) + |> should.equal([[1, 2, 3]]) - pub fn window_by_2_test() { - [1, 2, 3, 4] - |> list.window_by_2 - |> should.equal([#(1, 2), #(2, 3), #(3, 4)]) + [1, 2, 3] + |> list.window(4) + |> should.equal([]) - [1] - |> list.window_by_2 - |> should.equal([]) - } + [1, 2, 3, 4, 5] + |> list.window(3) + |> should.equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]]) +} - pub fn drop_while_test() { - [1, 2, 3, 4] - |> list.drop_while(fn(x) { x < 3 }) - |> should.equal([3, 4]) - } +pub fn window_by_2_test() { + [1, 2, 3, 4] + |> list.window_by_2 + |> should.equal([#(1, 2), #(2, 3), #(3, 4)]) - pub fn take_while_test() { - [1, 2, 3, 2, 4] - |> list.take_while(fn(x) { x < 3 }) - |> should.equal([1, 2]) - } + [1] + |> list.window_by_2 + |> should.equal([]) +} - pub fn chunk_test() { - [1, 2, 2, 3, 4, 4, 6, 7, 7] - |> list.chunk(by: fn(n) { n % 2 }) - |> should.equal([[1], [2, 2], [3], [4, 4, 6], [7, 7]]) - } +pub fn drop_while_test() { + [1, 2, 3, 4] + |> list.drop_while(fn(x) { x < 3 }) + |> should.equal([3, 4]) +} - pub fn sized_chunk_test() { - [1, 2, 3, 4, 5, 6] - |> list.sized_chunk(into: 2) - |> should.equal([[1, 2], [3, 4], [5, 6]]) +pub fn take_while_test() { + [1, 2, 3, 2, 4] + |> list.take_while(fn(x) { x < 3 }) + |> should.equal([1, 2]) +} - [1, 2, 3, 4, 5, 6, 7, 8] - |> list.sized_chunk(into: 3) - |> should.equal([[1, 2, 3], [4, 5, 6], [7, 8]]) - } +pub fn chunk_test() { + [1, 2, 2, 3, 4, 4, 6, 7, 7] + |> list.chunk(by: fn(n) { n % 2 }) + |> should.equal([[1], [2, 2], [3], [4, 4, 6], [7, 7]]) +} - pub fn reduce_test() { - [] - |> list.reduce(with: fn(x, y) { x + y }) - |> should.equal(Error(Nil)) +pub fn sized_chunk_test() { + [1, 2, 3, 4, 5, 6] + |> list.sized_chunk(into: 2) + |> should.equal([[1, 2], [3, 4], [5, 6]]) - [1, 2, 3, 4, 5] - |> list.reduce(with: fn(x, y) { x + y }) - |> should.equal(Ok(15)) - } + [1, 2, 3, 4, 5, 6, 7, 8] + |> list.sized_chunk(into: 3) + |> should.equal([[1, 2, 3], [4, 5, 6], [7, 8]]) +} - pub fn scan_test() { - [] - |> list.scan(from: 0, with: fn(i, acc) { i + acc }) - |> should.equal([]) - - [1, 2, 3, 4] - |> list.scan(from: 0, with: fn(i, acc) { 2 * i + acc }) - |> should.equal([2, 6, 12, 20]) - - [1, 2, 3, 4] - |> list.scan( - from: "", - with: fn(i, acc) { - case int.is_even(i) { - True -> "Even" - False -> "Odd" - } - |> string.append(acc, _) - }, - ) - |> should.equal(["Odd", "OddEven", "OddEvenOdd", "OddEvenOddEven"]) - } +pub fn reduce_test() { + [] + |> list.reduce(with: fn(x, y) { x + y }) + |> should.equal(Error(Nil)) - pub fn last_test() { - list.last([]) - |> should.equal(Error(Nil)) + [1, 2, 3, 4, 5] + |> list.reduce(with: fn(x, y) { x + y }) + |> should.equal(Ok(15)) +} - list.last([1, 2, 3, 4, 5]) - |> should.equal(Ok(5)) - } +pub fn scan_test() { + [] + |> list.scan(from: 0, with: fn(i, acc) { i + acc }) + |> should.equal([]) + + [1, 2, 3, 4] + |> list.scan(from: 0, with: fn(i, acc) { 2 * i + acc }) + |> should.equal([2, 6, 12, 20]) + + [1, 2, 3, 4] + |> list.scan( + from: [], + with: fn(i, acc) { + case int.is_even(i) { + True -> ["Even", ..acc] + False -> ["Odd", ..acc] + } + }, + ) + |> should.equal([ + ["Odd"], + ["Even", "Odd"], + ["Odd", "Even", "Odd"], + ["Even", "Odd", "Even", "Odd"], + ]) +} - pub fn combinations_test() { - list.combinations([1, 2, 3], by: 0) - |> should.equal([[]]) +pub fn last_test() { + list.last([]) + |> should.equal(Error(Nil)) - list.combinations([1, 2, 3], by: 1) - |> should.equal([[1], [2], [3]]) + list.last([1, 2, 3, 4, 5]) + |> should.equal(Ok(5)) +} - list.combinations([1, 2, 3], by: 2) - |> should.equal([[1, 2], [1, 3], [2, 3]]) +pub fn combinations_test() { + list.combinations([1, 2, 3], by: 0) + |> should.equal([[]]) - list.combinations([1, 2, 3], by: 3) - |> should.equal([[1, 2, 3]]) + list.combinations([1, 2, 3], by: 1) + |> should.equal([[1], [2], [3]]) - list.combinations([1, 2, 3], by: 4) - |> should.equal([]) + list.combinations([1, 2, 3], by: 2) + |> should.equal([[1, 2], [1, 3], [2, 3]]) - list.combinations([1, 2, 3, 4], 3) - |> should.equal([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]) - } + list.combinations([1, 2, 3], by: 3) + |> should.equal([[1, 2, 3]]) - pub fn combination_pairs_test() { - list.combination_pairs([1]) - |> should.equal([]) + list.combinations([1, 2, 3], by: 4) + |> should.equal([]) - list.combination_pairs([1, 2]) - |> should.equal([#(1, 2)]) + list.combinations([1, 2, 3, 4], 3) + |> should.equal([[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]) +} - list.combination_pairs([1, 2, 3]) - |> should.equal([#(1, 2), #(1, 3), #(2, 3)]) +pub fn combination_pairs_test() { + list.combination_pairs([1]) + |> should.equal([]) - list.combination_pairs([1, 2, 3, 4]) - |> should.equal([#(1, 2), #(1, 3), #(1, 4), #(2, 3), #(2, 4), #(3, 4)]) - } + list.combination_pairs([1, 2]) + |> should.equal([#(1, 2)]) - pub fn interleave_test() { - list.interleave([[1, 2], [101, 102]]) - |> should.equal([1, 101, 2, 102]) + list.combination_pairs([1, 2, 3]) + |> should.equal([#(1, 2), #(1, 3), #(2, 3)]) - list.interleave([[1, 2], [101, 102], [201, 202]]) - |> should.equal([1, 101, 201, 2, 102, 202]) + list.combination_pairs([1, 2, 3, 4]) + |> should.equal([#(1, 2), #(1, 3), #(1, 4), #(2, 3), #(2, 4), #(3, 4)]) +} - // Left over elements are added at the end - list.interleave([[1, 2, 3], [101, 102]]) - |> should.equal([1, 101, 2, 102, 3]) +pub fn interleave_test() { + list.interleave([[1, 2], [101, 102]]) + |> should.equal([1, 101, 2, 102]) - list.interleave([[1, 2], [101, 102, 103]]) - |> should.equal([1, 101, 2, 102, 103]) - } + list.interleave([[1, 2], [101, 102], [201, 202]]) + |> should.equal([1, 101, 201, 2, 102, 202]) - pub fn transpose_test() { - list.transpose([[1, 2, 3], [101, 102, 103]]) - |> should.equal([[1, 101], [2, 102], [3, 103]]) + // Left over elements are added at the end + list.interleave([[1, 2, 3], [101, 102]]) + |> should.equal([1, 101, 2, 102, 3]) - list.transpose([[1, 2, 3], [101, 102, 103], [201, 202, 203]]) - |> should.equal([[1, 101, 201], [2, 102, 202], [3, 103, 203]]) + list.interleave([[1, 2], [101, 102, 103]]) + |> should.equal([1, 101, 2, 102, 103]) +} - // Left over elements are still returned - list.transpose([[1, 2], [101, 102, 103]]) - |> should.equal([[1, 101], [2, 102], [103]]) +pub fn transpose_test() { + list.transpose([[1, 2, 3], [101, 102, 103]]) + |> should.equal([[1, 101], [2, 102], [3, 103]]) - list.transpose([[1, 2, 3], [101, 102], [201, 202, 203]]) - |> should.equal([[1, 101, 201], [2, 102, 202], [3, 203]]) - } + list.transpose([[1, 2, 3], [101, 102, 103], [201, 202, 203]]) + |> should.equal([[1, 101, 201], [2, 102, 202], [3, 103, 203]]) + + // Left over elements are still returned + list.transpose([[1, 2], [101, 102, 103]]) + |> should.equal([[1, 101], [2, 102], [103]]) + + list.transpose([[1, 2, 3], [101, 102], [201, 202, 203]]) + |> should.equal([[1, 101, 201], [2, 102, 202], [3, 203]]) } |