diff options
author | Louis Pilfold <louis@lpil.uk> | 2023-12-17 17:56:25 +0000 |
---|---|---|
committer | Louis Pilfold <louis@lpil.uk> | 2023-12-17 17:56:25 +0000 |
commit | 78980c30429d4aee2c77dbbb068bfe0d153d32ad (patch) | |
tree | f07eab731d4348b7240cf1976d12f9fd09807b6d | |
parent | 86c25554fca51fe485ba9ca436374854c06d64c0 (diff) | |
download | gleam_stdlib-78980c30429d4aee2c77dbbb068bfe0d153d32ad.tar.gz gleam_stdlib-78980c30429d4aee2c77dbbb068bfe0d153d32ad.zip |
Simplify random
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | src/gleam/float.gleam | 19 | ||||
-rw-r--r-- | src/gleam/int.gleam | 22 | ||||
-rw-r--r-- | src/gleam/list.gleam | 102 | ||||
-rw-r--r-- | test/gleam/float_test.gleam | 66 | ||||
-rw-r--r-- | test/gleam/int_test.gleam | 38 |
6 files changed, 95 insertions, 154 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index d95773a..64d52a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- The `int.random` function now takes a single argument. +- The `float.random` function no longer takes any arguments. - Changed `list.index_map` callback signature to `fn(a, Int) -> b` from `fn(Int, a) -> b`, to be consistent with `list.index_fold`. - Changed `iterator.index` to return `Iterator(#(a, Int))` instead of diff --git a/src/gleam/float.gleam b/src/gleam/float.gleam index 5d62419..e53074d 100644 --- a/src/gleam/float.gleam +++ b/src/gleam/float.gleam @@ -416,27 +416,22 @@ fn do_product(numbers: List(Float), initial: Float) -> Float { } } -/// Generates a random float between the given minimum and maximum values. +/// Generates a random float between the given zero (inclusive) and one +/// (exclusive). /// +/// On Erlang this updates the random state in the process dictionary. +/// See: <https://www.erlang.org/doc/man/rand.html#uniform-0> /// /// ## Examples /// /// ```gleam -/// > random(1.0, 5.0) -/// 2.646355926896028 +/// > random() +/// 0.646355926896028 /// ``` /// -pub fn random(min: Float, max: Float) -> Float { - do_random_uniform() *. { max -. min } +. min -} - -/// Returns a random float uniformly distributed in the value range -/// 0.0 =< X < 1.0 and updates the state in the process dictionary. -/// See: <https://www.erlang.org/doc/man/rand.html#uniform-0> -/// @external(erlang, "rand", "uniform") @external(javascript, "../gleam_stdlib.mjs", "random_uniform") -fn do_random_uniform() -> Float +pub fn random() -> Float /// Returns division of the inputs as a `Result`. /// diff --git a/src/gleam/int.gleam b/src/gleam/int.gleam index d93c16a..86c77fa 100644 --- a/src/gleam/int.gleam +++ b/src/gleam/int.gleam @@ -526,17 +526,29 @@ fn do_undigits( } } -/// Generates a random int between the given minimum and maximum values. +/// Generates a random int between zero and the given maximum. +/// +/// The lower number is inclusive, the upper number is exclusive. /// /// ## Examples /// /// ```gleam -/// > random(1, 5) -/// 2 +/// > random(10) +/// 4 +/// ``` +/// +/// ```gleam +/// > random(1) +/// 0 +/// ``` +/// +/// ```gleam +/// > random(-1) +/// -1 /// ``` /// -pub fn random(min: Int, max: Int) -> Int { - float.random(to_float(min), to_float(max)) +pub fn random(max: Int) -> Int { + { float.random() *. to_float(max) } |> float.floor() |> float.round() } diff --git a/src/gleam/list.gleam b/src/gleam/list.gleam index 5f62e58..f8ede18 100644 --- a/src/gleam/list.gleam +++ b/src/gleam/list.gleam @@ -428,15 +428,11 @@ pub fn map_fold( from acc: acc, with fun: fn(acc, a) -> #(acc, b), ) -> #(acc, List(b)) { - fold( - over: list, - from: #(acc, []), - with: fn(acc, item) { - let #(current_acc, items) = acc - let #(next_acc, next_item) = fun(current_acc, item) - #(next_acc, [next_item, ..items]) - }, - ) + fold(over: list, from: #(acc, []), with: fn(acc, item) { + let #(current_acc, items) = acc + let #(next_acc, next_item) = fun(current_acc, item) + #(next_acc, [next_item, ..items]) + }) |> pair.map_second(reverse) } @@ -1438,16 +1434,13 @@ pub fn key_find( in keyword_list: List(#(k, v)), find desired_key: k, ) -> Result(v, Nil) { - find_map( - keyword_list, - fn(keyword) { - let #(key, value) = keyword - case key == desired_key { - True -> Ok(value) - False -> Error(Nil) - } - }, - ) + find_map(keyword_list, fn(keyword) { + let #(key, value) = keyword + case key == desired_key { + True -> Ok(value) + False -> Error(Nil) + } + }) } /// Given a list of 2-element tuples, finds all tuples that have a given @@ -1472,16 +1465,13 @@ pub fn key_filter( in keyword_list: List(#(k, v)), find desired_key: k, ) -> List(v) { - filter_map( - keyword_list, - fn(keyword) { - let #(key, value) = keyword - case key == desired_key { - True -> Ok(value) - False -> Error(Nil) - } - }, - ) + filter_map(keyword_list, fn(keyword) { + let #(key, value) = keyword + case key == desired_key { + True -> Ok(value) + False -> Error(Nil) + } + }) } fn do_pop(haystack, predicate, checked) { @@ -1590,16 +1580,13 @@ pub fn key_pop( haystack: List(#(k, v)), key: k, ) -> Result(#(v, List(#(k, v))), Nil) { - pop_map( - haystack, - fn(entry) { - let #(k, v) = entry - case k { - k if k == key -> Ok(v) - _ -> Error(Nil) - } - }, - ) + pop_map(haystack, fn(entry) { + let #(k, v) = entry + case k { + k if k == key -> Ok(v) + _ -> Error(Nil) + } + }) } /// Given a list of 2-element tuples, inserts a key and value into the list. @@ -1720,15 +1707,12 @@ pub fn permutations(l: List(a)) -> List(List(a)) { l |> index_map(fn(i, i_idx) { l - |> index_fold( - [], - fn(acc, j, j_idx) { - case i_idx == j_idx { - True -> acc - False -> [j, ..acc] - } - }, - ) + |> index_fold([], fn(acc, j, j_idx) { + case i_idx == j_idx { + True -> acc + False -> [j, ..acc] + } + }) |> reverse |> permutations |> map(fn(permutation) { [i, ..permutation] }) @@ -2030,11 +2014,9 @@ pub fn combinations(items: List(a), by n: Int) -> List(List(a)) { let first_combinations = map(combinations(xs, n - 1), with: fn(com) { [x, ..com] }) |> reverse - fold( - first_combinations, - combinations(xs, n), - fn(acc, c) { [c, ..acc] }, - ) + fold(first_combinations, combinations(xs, n), fn(acc, c) { + [c, ..acc] + }) } } } @@ -2125,18 +2107,14 @@ fn do_shuffle_pair_unwrap(list: List(#(Float, a)), acc: List(a)) -> List(a) { fn do_shuffle_by_pair_indexes( list_of_pairs: List(#(Float, a)), ) -> List(#(Float, a)) { - sort( - list_of_pairs, - fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order { - float.compare(a_pair.0, b_pair.0) - }, - ) + sort(list_of_pairs, fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order { + float.compare(a_pair.0, b_pair.0) + }) } /// Takes a list, randomly sorts all items and returns the shuffled list. /// -/// This function uses Erlang's `:rand` module or Javascript's -/// `Math.random()` to calculate the index shuffling. +/// This function uses `float.random` to decide the order of the elements. /// /// ## Example /// @@ -2148,7 +2126,7 @@ fn do_shuffle_by_pair_indexes( /// pub fn shuffle(list: List(a)) -> List(a) { list - |> fold(from: [], with: fn(acc, a) { [#(float.random(0.0, 1.0), a), ..acc] }) + |> fold(from: [], with: fn(acc, a) { [#(float.random(), a), ..acc] }) |> do_shuffle_by_pair_indexes() |> do_shuffle_pair_unwrap([]) } diff --git a/test/gleam/float_test.gleam b/test/gleam/float_test.gleam index e3278d5..4e881f1 100644 --- a/test/gleam/float_test.gleam +++ b/test/gleam/float_test.gleam @@ -348,55 +348,27 @@ pub fn product_test() { } pub fn random_test() { - let test_boundaries = fn(_accumulator, _element) { - float.random(0.0, 0.0) - |> should.equal(0.0) - - float.random(0.0, 10.0) - |> fn(x) { x >=. 0.0 && x <. 10.0 } - |> should.be_true - - float.random(10.0, 0.0) - |> fn(x) { x >=. 0.0 && x <. 10.0 } - |> should.be_true - - float.random(0.0, -10.0) - |> fn(x) { x >=. -10.0 && x <. 0.0 } - |> should.be_true - - float.random(-10.0, 0.0) - |> fn(x) { x >=. -10.0 && x <. 0.0 } - |> should.be_true - - float.random(-10.0, 10.0) - |> fn(x) { x >=. -10.0 && x <. 10.0 } - |> should.be_true - - float.random(10.0, -10.0) - |> fn(x) { x >=. -10.0 && x <. 10.0 } - |> should.be_true - } - list.range(0, 100) - |> iterator.from_list() - |> iterator.fold(Nil, test_boundaries) - - let test_mean = fn(iterations: Int, min: Float, max: Float, tolerance: Float) { - let expected_average = float.sum([min, max]) /. 2.0 + let expected_average = 0.5 + let iterations = 10_000 + let sum = list.range(0, iterations) |> iterator.from_list() - |> iterator.fold( - from: 0.0, - with: fn(accumulator, _element) { accumulator +. float.random(min, max) }, - ) - |> fn(sum) { sum /. int.to_float(iterations) } - |> float.loosely_equals(expected_average, tolerance) - |> should.be_true - } - test_mean(100, 0.0, 0.0, 5.0) - test_mean(1000, 0.0, 100.0, 5.0) - test_mean(1000, -100.0, 100.0, 5.0) - test_mean(1000, -100.0, 0.0, 5.0) - test_mean(1000, 0.0, -100.0, 5.0) + |> iterator.fold(from: 0.0, with: fn(accumulator, _element) { + let i = float.random() + + { i <. 1.0 } + |> should.be_true + { i >=. 0.0 } + |> should.be_true + + accumulator +. i + }) + let average = sum /. int.to_float(iterations) + + { average <. expected_average +. 0.1 } + |> should.be_true + { average >. expected_average -. 0.1 } + |> should.be_true } pub fn divide_test() { diff --git a/test/gleam/int_test.gleam b/test/gleam/int_test.gleam index b9aac9d..e3ee369 100644 --- a/test/gleam/int_test.gleam +++ b/test/gleam/int_test.gleam @@ -394,44 +394,26 @@ pub fn undigits_test() { pub fn random_test() { let test_boundaries = fn(_accumulator, _element) { - int.random(0, 0) + int.random(0) |> should.equal(0) - int.random(-1, 0) - |> list.contains([-1, 0], _) - |> should.be_true + int.random(1) + |> should.equal(0) + + int.random(-1) + |> should.equal(-1) - int.random(-1, 1) - |> list.contains([-1, 0], _) + int.random(2) + |> list.contains([0, 1], _) |> should.be_true - int.random(-1, 2) - |> list.contains([-1, 0, 1], _) + int.random(3) + |> list.contains([0, 1, 2], _) |> should.be_true } list.range(0, 100) |> iterator.from_list |> iterator.fold(Nil, test_boundaries) - - let test_average = fn(iterations: Int, min: Int, max: Int, tolerance: Int) { - let expected_average = int.sum([min, max]) / 2 - list.range(0, iterations) - |> iterator.from_list - |> iterator.fold( - from: 0, - with: fn(accumulator, _element) { accumulator + int.random(min, max) }, - ) - |> fn(sum) { sum / iterations } - |> fn(average) { - average - tolerance <= expected_average || average + tolerance >= expected_average - } - |> should.be_true - } - test_average(100, 0, 0, 5) - test_average(1000, 0, 100, 5) - test_average(1000, -100, 100, 5) - test_average(1000, -100, 0, 5) - test_average(1000, 0, -100, 5) } pub fn divide_test() { |