From 34d7d8697123eada9fb1d4a36ee1aee2c71a8b9d Mon Sep 17 00:00:00 2001 From: Louis Pilfold Date: Mon, 26 Jul 2021 19:37:38 +0100 Subject: Gleam native string padding. Iterator tests --- src/gleam/iterator.gleam | 286 +++++++++++++++++++++++------------------------ src/gleam/string.gleam | 66 ++++++----- src/gleam_stdlib.js | 8 -- 3 files changed, 175 insertions(+), 185 deletions(-) (limited to 'src') diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam index e7405bb..4f8b7ff 100644 --- a/src/gleam/iterator.gleam +++ b/src/gleam/iterator.gleam @@ -922,159 +922,159 @@ if erlang { |> fold(map.new(), group_updater(key)) |> map.map_values(fn(_, group) { list.reverse(group) }) } +} - /// This function acts similar to fold, but does not take an initial state. - /// Instead, it starts from the first yielded element - /// and combines it with each subsequent element in turn using the given function. - /// The function is called as f(current_element, accumulator). - /// - /// Returns `Ok` to indicate a successful run, and `Error` if called on an empty iterator. - /// - /// ## Examples - /// - /// > from_list([]) |> reduce(fn(x, y) { x + y }) - /// Error(Nil) - /// - /// > from_list([1, 2, 3, 4, 5]) |> reduce(fn(x, y) { x + y }) - /// Ok(15) - /// - pub fn reduce( - over iterator: Iterator(e), - with f: fn(e, e) -> e, - ) -> Result(e, Nil) { - case iterator.continuation() { - Stop -> Error(Nil) - Continue(e, next) -> - do_fold(next, f, e) - |> Ok - } +/// This function acts similar to fold, but does not take an initial state. +/// Instead, it starts from the first yielded element +/// and combines it with each subsequent element in turn using the given function. +/// The function is called as f(current_element, accumulator). +/// +/// Returns `Ok` to indicate a successful run, and `Error` if called on an empty iterator. +/// +/// ## Examples +/// +/// > from_list([]) |> reduce(fn(x, y) { x + y }) +/// Error(Nil) +/// +/// > from_list([1, 2, 3, 4, 5]) |> reduce(fn(x, y) { x + y }) +/// Ok(15) +/// +pub fn reduce( + over iterator: Iterator(e), + with f: fn(e, e) -> e, +) -> Result(e, Nil) { + case iterator.continuation() { + Stop -> Error(Nil) + Continue(e, next) -> + do_fold(next, f, e) + |> Ok } +} - /// Returns the last element in the given iterator. - /// - /// Returns `Error(Nil)` if the iterator is empty. - /// - /// This function runs in linear time. - /// - /// ## Examples - /// - /// > empty() |> last - /// Error(Nil) - /// - /// > range(1, 10) |> last - /// Ok(9) - /// - pub fn last(iterator: Iterator(element)) -> Result(element, Nil) { - iterator - |> reduce(fn(elem, _) { elem }) - } +/// Returns the last element in the given iterator. +/// +/// Returns `Error(Nil)` if the iterator is empty. +/// +/// This function runs in linear time. +/// +/// ## Examples +/// +/// > empty() |> last +/// Error(Nil) +/// +/// > range(1, 10) |> last +/// Ok(9) +/// +pub fn last(iterator: Iterator(element)) -> Result(element, Nil) { + iterator + |> reduce(fn(elem, _) { elem }) +} - /// Creates an iterator that yields no elements. - /// - /// ## Examples - /// - /// > empty() |> to_list - /// [] - /// - pub fn empty() -> Iterator(element) { - Iterator(stop) - } +/// Creates an iterator that yields no elements. +/// +/// ## Examples +/// +/// > empty() |> to_list +/// [] +/// +pub fn empty() -> Iterator(element) { + Iterator(stop) +} - /// Creates an iterator that yields exactly one element provided by calling the given function. - /// - /// ## Examples - /// - /// > once(fn() { 1 }) |> to_list - /// [1] - /// - pub fn once(f: fn() -> element) -> Iterator(element) { - fn() { Continue(f(), stop) } - |> Iterator - } +/// Creates an iterator that yields exactly one element provided by calling the given function. +/// +/// ## Examples +/// +/// > once(fn() { 1 }) |> to_list +/// [1] +/// +pub fn once(f: fn() -> element) -> Iterator(element) { + fn() { Continue(f(), stop) } + |> Iterator +} - /// Creates an iterator that yields the given element exactly once. - /// - /// ## Examples - /// - /// > single(1) |> to_list - /// [1] - /// - pub fn single(elem: element) -> Iterator(element) { - once(fn() { elem }) - } +/// Creates an iterator that yields the given element exactly once. +/// +/// ## Examples +/// +/// > single(1) |> to_list +/// [1] +/// +pub fn single(elem: element) -> Iterator(element) { + once(fn() { elem }) +} - fn do_interleave( - current: fn() -> Action(element), - next: fn() -> Action(element), - ) -> Action(element) { - case current() { - Stop -> next() - Continue(e, next_other) -> - Continue(e, fn() { do_interleave(next, next_other) }) - } +fn do_interleave( + current: fn() -> Action(element), + next: fn() -> Action(element), +) -> Action(element) { + case current() { + Stop -> next() + Continue(e, next_other) -> + Continue(e, fn() { do_interleave(next, next_other) }) } +} - /// Creates an iterator that alternates between the two given iterators - /// until both have run out. - /// - /// ## Examples - /// - /// > from_list([1, 2, 3, 4]) |> interleave(from_list([11, 12, 13, 14])) |> to_list - /// [1, 11, 2, 12, 3, 13, 4, 14] - /// - /// > from_list([1, 2, 3, 4]) |> interleave(from_list([100])) |> to_list - /// [1, 100, 2, 3, 4] - /// - pub fn interleave( - left: Iterator(element), - with right: Iterator(element), - ) -> Iterator(element) { - fn() { do_interleave(left.continuation, right.continuation) } - |> Iterator - } +/// Creates an iterator that alternates between the two given iterators +/// until both have run out. +/// +/// ## Examples +/// +/// > from_list([1, 2, 3, 4]) |> interleave(from_list([11, 12, 13, 14])) |> to_list +/// [1, 11, 2, 12, 3, 13, 4, 14] +/// +/// > from_list([1, 2, 3, 4]) |> interleave(from_list([100])) |> to_list +/// [1, 100, 2, 3, 4] +/// +pub fn interleave( + left: Iterator(element), + with right: Iterator(element), +) -> Iterator(element) { + fn() { do_interleave(left.continuation, right.continuation) } + |> Iterator +} - fn do_fold_until( - continuation: fn() -> Action(e), - f: fn(e, acc) -> list.ContinueOrStop(acc), - accumulator: acc, - ) -> acc { - case continuation() { - Stop -> accumulator - Continue(elem, next) -> - case f(elem, accumulator) { - list.Continue(accumulator) -> do_fold_until(next, f, accumulator) - list.Stop(accumulator) -> accumulator - } - } +fn do_fold_until( + continuation: fn() -> Action(e), + f: fn(e, acc) -> list.ContinueOrStop(acc), + accumulator: acc, +) -> acc { + case continuation() { + Stop -> accumulator + Continue(elem, next) -> + case f(elem, accumulator) { + list.Continue(accumulator) -> do_fold_until(next, f, accumulator) + list.Stop(accumulator) -> accumulator + } } +} - /// Like `fold`, `fold_until` reduces an iterator of elements into a single value by calling a given - /// function on each element in turn, but uses a `list.ContinueOrStop` to determine - /// whether or not to keep iterating. - /// - /// If called on an iterator of infinite length then this function will only ever - /// return if the give function returns list.Stop. - /// - /// - /// ## Examples - /// > let f = fn(e, acc) { - /// > case e { - /// > _ if e < 4 -> list.Continue(e + acc) - /// > _ -> list.Stop(acc) - /// > } - /// > } - /// > - /// > [1, 2, 3, 4] - /// > |> from_list - /// > |> iterator.fold_until(from: acc, with: f) - /// 6 - /// - pub fn fold_until( - over iterator: Iterator(e), - from initial: acc, - with f: fn(e, acc) -> list.ContinueOrStop(acc), - ) -> acc { - iterator.continuation - |> do_fold_until(f, initial) - } +/// Like `fold`, `fold_until` reduces an iterator of elements into a single value by calling a given +/// function on each element in turn, but uses a `list.ContinueOrStop` to determine +/// whether or not to keep iterating. +/// +/// If called on an iterator of infinite length then this function will only ever +/// return if the give function returns list.Stop. +/// +/// +/// ## Examples +/// > let f = fn(e, acc) { +/// > case e { +/// > _ if e < 4 -> list.Continue(e + acc) +/// > _ -> list.Stop(acc) +/// > } +/// > } +/// > +/// > [1, 2, 3, 4] +/// > |> from_list +/// > |> iterator.fold_until(from: acc, with: f) +/// 6 +/// +pub fn fold_until( + over iterator: Iterator(e), + from initial: acc, + with f: fn(e, acc) -> list.ContinueOrStop(acc), +) -> acc { + iterator.continuation + |> do_fold_until(f, initial) } diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam index 0703dd7..d9d2c63 100644 --- a/src/gleam/string.gleam +++ b/src/gleam/string.gleam @@ -2,7 +2,7 @@ //// text surrounded by `"double quotes"`. import gleam/string_builder -import gleam/iterator +import gleam/iterator.{Iterator} import gleam/list import gleam/order import gleam/result @@ -494,28 +494,13 @@ pub fn join(strings: List(String), with separator: String) -> String { /// > pad_left("121", to: 2, with: ".") /// "121" /// -pub fn pad_left(string: String, to length: Int, with pad_string: String) { - do_pad_left(string, length, pad_string) -} - -if erlang { - type Direction { - Leading - Trailing - Both - } - - fn do_pad_left(string: String, to length: Int, with pad_string: String) { - erl_pad(string, length, Leading, pad_string) - } - - external fn erl_pad(String, Int, Direction, String) -> String = - "gleam_stdlib" "string_pad" -} - -if javascript { - external fn do_pad_left(String, Int, String) -> String = - "../gleam_stdlib.js" "pad_left" +pub fn pad_left(string: String, to desired_length: Int, with pad_string: String) { + let current_length = length(string) + let to_pad_length = desired_length - current_length + padding(to_pad_length, pad_string) + |> iterator.append(iterator.single(string)) + |> iterator.to_list + |> concat } /// Pads a string on the right until it has a given length. @@ -531,19 +516,26 @@ if javascript { /// > pad_right("121", to: 2, with: ".") /// "121" /// -pub fn pad_right(string: String, to length: Int, with pad_string: String) { - do_pad_right(string, length, pad_string) -} - -if erlang { - fn do_pad_right(string: String, to length: Int, with pad_string: String) { - erl_pad(string, length, Trailing, pad_string) - } +pub fn pad_right( + string: String, + to desired_length: Int, + with pad_string: String, +) { + let current_length = length(string) + let to_pad_length = desired_length - current_length + iterator.single(string) + |> iterator.append(padding(to_pad_length, pad_string)) + |> iterator.to_list + |> concat } -if javascript { - external fn do_pad_right(String, Int, String) -> String = - "../gleam_stdlib.js" "pad_right" +fn padding(size: Int, pad_string: String) -> Iterator(String) { + let pad_length = length(pad_string) + let num_pads = size / pad_length + let extra = size % pad_length + iterator.repeat(pad_string) + |> iterator.take(num_pads) + |> iterator.append(iterator.single(slice(pad_string, 0, extra))) } /// Removes whitespace on both sides of a String. @@ -562,6 +554,12 @@ if erlang { erl_trim(string, Both) } + type Direction { + Leading + Trailing + Both + } + external fn erl_trim(String, Direction) -> String = "string" "trim" } diff --git a/src/gleam_stdlib.js b/src/gleam_stdlib.js index 65dd45c..89908de 100644 --- a/src/gleam_stdlib.js +++ b/src/gleam_stdlib.js @@ -158,11 +158,3 @@ export function trim_left(string) { export function trim_right(string) { return string.trimRight(); } - -export function pad_left(string, length, pad) { - return string.padStart(length, pad); -} - -export function pad_right(string, length, pad) { - return string.padEnd(length, pad); -} -- cgit v1.2.3