diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | src/gleam/list.gleam | 38 | ||||
-rw-r--r-- | test/gleam/list_test.gleam | 14 |
3 files changed, 53 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 94a8644..8ac7123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -- The `list` modules gains the `window`, and `window_by_2` functions. +- The `list` modules gains the `fold_until`, `window`, and `window_by_2` functions. - The `int` module gains the `clamp` function. - The `float` module gains the `clamp` function. diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam index f32a20b..b6afb3c 100644 --- a/src/gleam/list.gleam +++ b/src/gleam/list.gleam @@ -517,6 +517,44 @@ pub fn try_fold( } } +pub type ContinueOrStop(a) { + Continue(a) + Stop(a) +} + +/// 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 + } + } +} + /// Find the first element in a given list for which the given function returns /// True. /// diff --git a/test/gleam/list_test.gleam b/test/gleam/list_test.gleam index 2aa1b3e..3277e47 100644 --- a/test/gleam/list_test.gleam +++ b/test/gleam/list_test.gleam @@ -177,6 +177,20 @@ pub fn index_fold_test() { |> should.equal([tuple(2, "c"), tuple(1, "b"), tuple(0, "a")]) } +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 try_fold_test() { [1, 2, 3] |> list.try_fold( |