diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | src/gleam/list.gleam | 30 | ||||
-rw-r--r-- | test/gleam/list_test.gleam | 23 |
3 files changed, 54 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d0cc1c..57a3f4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ functions and their typed equivalents `typed_tuple3`, `typed_tuple4`, `typed_tuple5`, `typed_tuple6`. - The `list` module gains the `drop_while`, `map_fold`, `take_while`, `reduce`, - `chunk` and `sized_chunk` functions. + `chunk`, `sized_chunk` and `scan` functions. - The `iterator` module gains the `index`, `iterate`, `zip`, `scan`, `take_while`, `drop_while`, `chunk`, `sized_chunk`, `intersperse`, `reduce`, `any` and `all` functions. - Breaking change in `iterator.take`. Now it returns an iterator instead of a list. diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam index dc03ec1..9f88063 100644 --- a/src/gleam/list.gleam +++ b/src/gleam/list.gleam @@ -1401,3 +1401,33 @@ pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) { |> 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) + } + } +} + +/// 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) +} diff --git a/test/gleam/list_test.gleam b/test/gleam/list_test.gleam index bf93203..572812c 100644 --- a/test/gleam/list_test.gleam +++ b/test/gleam/list_test.gleam @@ -622,3 +622,26 @@ pub fn reduce_test() { |> list.reduce(with: fn(x, y) { x + y }) |> should.equal(Ok(15)) } + +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"]) +} |