aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2023-12-17 17:56:25 +0000
committerLouis Pilfold <louis@lpil.uk>2023-12-17 17:56:25 +0000
commit78980c30429d4aee2c77dbbb068bfe0d153d32ad (patch)
treef07eab731d4348b7240cf1976d12f9fd09807b6d
parent86c25554fca51fe485ba9ca436374854c06d64c0 (diff)
downloadgleam_stdlib-78980c30429d4aee2c77dbbb068bfe0d153d32ad.tar.gz
gleam_stdlib-78980c30429d4aee2c77dbbb068bfe0d153d32ad.zip
Simplify random
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/gleam/float.gleam19
-rw-r--r--src/gleam/int.gleam22
-rw-r--r--src/gleam/list.gleam102
-rw-r--r--test/gleam/float_test.gleam66
-rw-r--r--test/gleam/int_test.gleam38
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() {