aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gleam/iterator.gleam35
-rw-r--r--src/gleam/list.gleam24
-rw-r--r--test/gleam/iterator_test.gleam10
-rw-r--r--test/gleam/list_test.gleam10
4 files changed, 75 insertions, 4 deletions
diff --git a/src/gleam/iterator.gleam b/src/gleam/iterator.gleam
index f9e39bb..fa49b87 100644
--- a/src/gleam/iterator.gleam
+++ b/src/gleam/iterator.gleam
@@ -115,12 +115,12 @@ pub fn from_list(list: List(element)) -> Iterator(element) {
// Consuming Iterators
fn do_fold(
continuation: fn() -> Action(e),
- initial: acc,
f: fn(e, acc) -> acc,
+ accumulator: acc,
) -> acc {
case continuation() {
- Continue(element, iterator) -> do_fold(iterator, f(element, initial), f)
- Stop -> initial
+ Continue(elem, next) -> do_fold(next, f, f(elem, accumulator))
+ Stop -> accumulator
}
}
@@ -146,7 +146,7 @@ pub fn fold(
with f: fn(e, acc) -> acc,
) -> acc {
iterator.continuation
- |> do_fold(initial, f)
+ |> do_fold(f, initial)
}
// TODO: test
@@ -919,3 +919,30 @@ pub fn group(
|> 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
+ }
+}
diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam
index cb7d940..972da23 100644
--- a/src/gleam/list.gleam
+++ b/src/gleam/list.gleam
@@ -1377,3 +1377,27 @@ fn do_sized_chunk(
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
+ }
+}
diff --git a/test/gleam/iterator_test.gleam b/test/gleam/iterator_test.gleam
index 81a2908..492bb1e 100644
--- a/test/gleam/iterator_test.gleam
+++ b/test/gleam/iterator_test.gleam
@@ -373,3 +373,13 @@ pub fn group_test() {
tuple(2, [2, 5]),
]))
}
+
+pub fn reduce_test() {
+ iterator.from_list([])
+ |> iterator.reduce(with: fn(x, y) { x + y })
+ |> should.equal(Error(Nil))
+
+ iterator.from_list([1, 2, 3, 4, 5])
+ |> iterator.reduce(with: fn(x, y) { x + y })
+ |> should.equal(Ok(15))
+}
diff --git a/test/gleam/list_test.gleam b/test/gleam/list_test.gleam
index b7470ac..fc8ea11 100644
--- a/test/gleam/list_test.gleam
+++ b/test/gleam/list_test.gleam
@@ -612,3 +612,13 @@ pub fn sized_chunk_test() {
|> list.sized_chunk(into: 3)
|> should.equal([[1, 2, 3], [4, 5, 6], [7, 8]])
}
+
+pub fn reduce_test() {
+ []
+ |> list.reduce(with: fn(x, y) { x + y })
+ |> should.equal(Error(Nil))
+
+ [1, 2, 3, 4, 5]
+ |> list.reduce(with: fn(x, y) { x + y })
+ |> should.equal(Ok(15))
+}