diff --git a/CHANGELOG.md b/CHANGELOG.md index c00c6aeb..ede590a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +- The performance of the `string.repeat` function has been improved. It now runs + in loglinear time. + ## v0.62.1 - 2025-08-07 - `string.inspect` now shows Erlang atoms as `atom.create("value")`, to match diff --git a/src/gleam/string.gleam b/src/gleam/string.gleam index c1e31c46..e00d0327 100644 --- a/src/gleam/string.gleam +++ b/src/gleam/string.gleam @@ -406,7 +406,7 @@ fn concat_loop(strings: List(String), accumulator: String) -> String { /// Creates a new `String` by repeating a `String` a given number of times. /// -/// This function runs in linear time. +/// This function runs in loglinear time. /// /// ## Examples /// @@ -416,13 +416,26 @@ fn concat_loop(strings: List(String), accumulator: String) -> String { /// ``` /// pub fn repeat(string: String, times times: Int) -> String { - repeat_loop(string, times, "") + case times <= 0 { + True -> "" + False -> repeat_loop(string, times, string, "") + } } -fn repeat_loop(string: String, times: Int, acc: String) -> String { +fn repeat_loop( + string: String, + times: Int, + doubling_acc: String, + acc: String, +) -> String { + let acc = case times % 2 { + 0 -> acc + _ -> acc <> doubling_acc + } + let times = times / 2 case times <= 0 { True -> acc - False -> repeat_loop(string, times - 1, acc <> string) + False -> repeat_loop(string, times, doubling_acc <> doubling_acc, acc) } } diff --git a/test/gleam/string_test.gleam b/test/gleam/string_test.gleam index ed17608f..1cfc784e 100644 --- a/test/gleam/string_test.gleam +++ b/test/gleam/string_test.gleam @@ -106,8 +106,14 @@ pub fn concat_emoji_test() { } pub fn repeat_test() { + assert string.repeat("hi", times: 1) == "hi" + + assert string.repeat("hi", times: 2) == "hihi" + assert string.repeat("hi", times: 3) == "hihihi" + assert string.repeat("a", times: 10_001) |> string.length == 10_001 + assert string.repeat("hi", 0) == "" assert string.repeat("hi", -1) == ""