Skip to content

Commit 78980c3

Browse files
committed
Simplify random
1 parent 86c2555 commit 78980c3

File tree

6 files changed

+95
-154
lines changed

6 files changed

+95
-154
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- The `int.random` function now takes a single argument.
6+
- The `float.random` function no longer takes any arguments.
57
- Changed `list.index_map` callback signature to `fn(a, Int) -> b` from
68
`fn(Int, a) -> b`, to be consistent with `list.index_fold`.
79
- Changed `iterator.index` to return `Iterator(#(a, Int))` instead of

src/gleam/float.gleam

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -416,27 +416,22 @@ fn do_product(numbers: List(Float), initial: Float) -> Float {
416416
}
417417
}
418418

419-
/// Generates a random float between the given minimum and maximum values.
419+
/// Generates a random float between the given zero (inclusive) and one
420+
/// (exclusive).
420421
///
422+
/// On Erlang this updates the random state in the process dictionary.
423+
/// See: <https://www.erlang.org/doc/man/rand.html#uniform-0>
421424
///
422425
/// ## Examples
423426
///
424427
/// ```gleam
425-
/// > random(1.0, 5.0)
426-
/// 2.646355926896028
428+
/// > random()
429+
/// 0.646355926896028
427430
/// ```
428431
///
429-
pub fn random(min: Float, max: Float) -> Float {
430-
do_random_uniform() *. { max -. min } +. min
431-
}
432-
433-
/// Returns a random float uniformly distributed in the value range
434-
/// 0.0 =< X < 1.0 and updates the state in the process dictionary.
435-
/// See: <https://www.erlang.org/doc/man/rand.html#uniform-0>
436-
///
437432
@external(erlang, "rand", "uniform")
438433
@external(javascript, "../gleam_stdlib.mjs", "random_uniform")
439-
fn do_random_uniform() -> Float
434+
pub fn random() -> Float
440435

441436
/// Returns division of the inputs as a `Result`.
442437
///

src/gleam/int.gleam

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -526,17 +526,29 @@ fn do_undigits(
526526
}
527527
}
528528

529-
/// Generates a random int between the given minimum and maximum values.
529+
/// Generates a random int between zero and the given maximum.
530+
///
531+
/// The lower number is inclusive, the upper number is exclusive.
530532
///
531533
/// ## Examples
532534
///
533535
/// ```gleam
534-
/// > random(1, 5)
535-
/// 2
536+
/// > random(10)
537+
/// 4
538+
/// ```
539+
///
540+
/// ```gleam
541+
/// > random(1)
542+
/// 0
543+
/// ```
544+
///
545+
/// ```gleam
546+
/// > random(-1)
547+
/// -1
536548
/// ```
537549
///
538-
pub fn random(min: Int, max: Int) -> Int {
539-
float.random(to_float(min), to_float(max))
550+
pub fn random(max: Int) -> Int {
551+
{ float.random() *. to_float(max) }
540552
|> float.floor()
541553
|> float.round()
542554
}

src/gleam/list.gleam

Lines changed: 40 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -428,15 +428,11 @@ pub fn map_fold(
428428
from acc: acc,
429429
with fun: fn(acc, a) -> #(acc, b),
430430
) -> #(acc, List(b)) {
431-
fold(
432-
over: list,
433-
from: #(acc, []),
434-
with: fn(acc, item) {
435-
let #(current_acc, items) = acc
436-
let #(next_acc, next_item) = fun(current_acc, item)
437-
#(next_acc, [next_item, ..items])
438-
},
439-
)
431+
fold(over: list, from: #(acc, []), with: fn(acc, item) {
432+
let #(current_acc, items) = acc
433+
let #(next_acc, next_item) = fun(current_acc, item)
434+
#(next_acc, [next_item, ..items])
435+
})
440436
|> pair.map_second(reverse)
441437
}
442438

@@ -1438,16 +1434,13 @@ pub fn key_find(
14381434
in keyword_list: List(#(k, v)),
14391435
find desired_key: k,
14401436
) -> Result(v, Nil) {
1441-
find_map(
1442-
keyword_list,
1443-
fn(keyword) {
1444-
let #(key, value) = keyword
1445-
case key == desired_key {
1446-
True -> Ok(value)
1447-
False -> Error(Nil)
1448-
}
1449-
},
1450-
)
1437+
find_map(keyword_list, fn(keyword) {
1438+
let #(key, value) = keyword
1439+
case key == desired_key {
1440+
True -> Ok(value)
1441+
False -> Error(Nil)
1442+
}
1443+
})
14511444
}
14521445

14531446
/// Given a list of 2-element tuples, finds all tuples that have a given
@@ -1472,16 +1465,13 @@ pub fn key_filter(
14721465
in keyword_list: List(#(k, v)),
14731466
find desired_key: k,
14741467
) -> List(v) {
1475-
filter_map(
1476-
keyword_list,
1477-
fn(keyword) {
1478-
let #(key, value) = keyword
1479-
case key == desired_key {
1480-
True -> Ok(value)
1481-
False -> Error(Nil)
1482-
}
1483-
},
1484-
)
1468+
filter_map(keyword_list, fn(keyword) {
1469+
let #(key, value) = keyword
1470+
case key == desired_key {
1471+
True -> Ok(value)
1472+
False -> Error(Nil)
1473+
}
1474+
})
14851475
}
14861476

14871477
fn do_pop(haystack, predicate, checked) {
@@ -1590,16 +1580,13 @@ pub fn key_pop(
15901580
haystack: List(#(k, v)),
15911581
key: k,
15921582
) -> Result(#(v, List(#(k, v))), Nil) {
1593-
pop_map(
1594-
haystack,
1595-
fn(entry) {
1596-
let #(k, v) = entry
1597-
case k {
1598-
k if k == key -> Ok(v)
1599-
_ -> Error(Nil)
1600-
}
1601-
},
1602-
)
1583+
pop_map(haystack, fn(entry) {
1584+
let #(k, v) = entry
1585+
case k {
1586+
k if k == key -> Ok(v)
1587+
_ -> Error(Nil)
1588+
}
1589+
})
16031590
}
16041591

16051592
/// 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)) {
17201707
l
17211708
|> index_map(fn(i, i_idx) {
17221709
l
1723-
|> index_fold(
1724-
[],
1725-
fn(acc, j, j_idx) {
1726-
case i_idx == j_idx {
1727-
True -> acc
1728-
False -> [j, ..acc]
1729-
}
1730-
},
1731-
)
1710+
|> index_fold([], fn(acc, j, j_idx) {
1711+
case i_idx == j_idx {
1712+
True -> acc
1713+
False -> [j, ..acc]
1714+
}
1715+
})
17321716
|> reverse
17331717
|> permutations
17341718
|> map(fn(permutation) { [i, ..permutation] })
@@ -2030,11 +2014,9 @@ pub fn combinations(items: List(a), by n: Int) -> List(List(a)) {
20302014
let first_combinations =
20312015
map(combinations(xs, n - 1), with: fn(com) { [x, ..com] })
20322016
|> reverse
2033-
fold(
2034-
first_combinations,
2035-
combinations(xs, n),
2036-
fn(acc, c) { [c, ..acc] },
2037-
)
2017+
fold(first_combinations, combinations(xs, n), fn(acc, c) {
2018+
[c, ..acc]
2019+
})
20382020
}
20392021
}
20402022
}
@@ -2125,18 +2107,14 @@ fn do_shuffle_pair_unwrap(list: List(#(Float, a)), acc: List(a)) -> List(a) {
21252107
fn do_shuffle_by_pair_indexes(
21262108
list_of_pairs: List(#(Float, a)),
21272109
) -> List(#(Float, a)) {
2128-
sort(
2129-
list_of_pairs,
2130-
fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order {
2131-
float.compare(a_pair.0, b_pair.0)
2132-
},
2133-
)
2110+
sort(list_of_pairs, fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order {
2111+
float.compare(a_pair.0, b_pair.0)
2112+
})
21342113
}
21352114

21362115
/// Takes a list, randomly sorts all items and returns the shuffled list.
21372116
///
2138-
/// This function uses Erlang's `:rand` module or Javascript's
2139-
/// `Math.random()` to calculate the index shuffling.
2117+
/// This function uses `float.random` to decide the order of the elements.
21402118
///
21412119
/// ## Example
21422120
///
@@ -2148,7 +2126,7 @@ fn do_shuffle_by_pair_indexes(
21482126
///
21492127
pub fn shuffle(list: List(a)) -> List(a) {
21502128
list
2151-
|> fold(from: [], with: fn(acc, a) { [#(float.random(0.0, 1.0), a), ..acc] })
2129+
|> fold(from: [], with: fn(acc, a) { [#(float.random(), a), ..acc] })
21522130
|> do_shuffle_by_pair_indexes()
21532131
|> do_shuffle_pair_unwrap([])
21542132
}

test/gleam/float_test.gleam

Lines changed: 19 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -348,55 +348,27 @@ pub fn product_test() {
348348
}
349349

350350
pub fn random_test() {
351-
let test_boundaries = fn(_accumulator, _element) {
352-
float.random(0.0, 0.0)
353-
|> should.equal(0.0)
354-
355-
float.random(0.0, 10.0)
356-
|> fn(x) { x >=. 0.0 && x <. 10.0 }
357-
|> should.be_true
358-
359-
float.random(10.0, 0.0)
360-
|> fn(x) { x >=. 0.0 && x <. 10.0 }
361-
|> should.be_true
362-
363-
float.random(0.0, -10.0)
364-
|> fn(x) { x >=. -10.0 && x <. 0.0 }
365-
|> should.be_true
366-
367-
float.random(-10.0, 0.0)
368-
|> fn(x) { x >=. -10.0 && x <. 0.0 }
369-
|> should.be_true
370-
371-
float.random(-10.0, 10.0)
372-
|> fn(x) { x >=. -10.0 && x <. 10.0 }
373-
|> should.be_true
374-
375-
float.random(10.0, -10.0)
376-
|> fn(x) { x >=. -10.0 && x <. 10.0 }
377-
|> should.be_true
378-
}
379-
list.range(0, 100)
380-
|> iterator.from_list()
381-
|> iterator.fold(Nil, test_boundaries)
382-
383-
let test_mean = fn(iterations: Int, min: Float, max: Float, tolerance: Float) {
384-
let expected_average = float.sum([min, max]) /. 2.0
351+
let expected_average = 0.5
352+
let iterations = 10_000
353+
let sum =
385354
list.range(0, iterations)
386355
|> iterator.from_list()
387-
|> iterator.fold(
388-
from: 0.0,
389-
with: fn(accumulator, _element) { accumulator +. float.random(min, max) },
390-
)
391-
|> fn(sum) { sum /. int.to_float(iterations) }
392-
|> float.loosely_equals(expected_average, tolerance)
393-
|> should.be_true
394-
}
395-
test_mean(100, 0.0, 0.0, 5.0)
396-
test_mean(1000, 0.0, 100.0, 5.0)
397-
test_mean(1000, -100.0, 100.0, 5.0)
398-
test_mean(1000, -100.0, 0.0, 5.0)
399-
test_mean(1000, 0.0, -100.0, 5.0)
356+
|> iterator.fold(from: 0.0, with: fn(accumulator, _element) {
357+
let i = float.random()
358+
359+
{ i <. 1.0 }
360+
|> should.be_true
361+
{ i >=. 0.0 }
362+
|> should.be_true
363+
364+
accumulator +. i
365+
})
366+
let average = sum /. int.to_float(iterations)
367+
368+
{ average <. expected_average +. 0.1 }
369+
|> should.be_true
370+
{ average >. expected_average -. 0.1 }
371+
|> should.be_true
400372
}
401373

402374
pub fn divide_test() {

test/gleam/int_test.gleam

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -394,44 +394,26 @@ pub fn undigits_test() {
394394

395395
pub fn random_test() {
396396
let test_boundaries = fn(_accumulator, _element) {
397-
int.random(0, 0)
397+
int.random(0)
398398
|> should.equal(0)
399399

400-
int.random(-1, 0)
401-
|> list.contains([-1, 0], _)
402-
|> should.be_true
400+
int.random(1)
401+
|> should.equal(0)
402+
403+
int.random(-1)
404+
|> should.equal(-1)
403405

404-
int.random(-1, 1)
405-
|> list.contains([-1, 0], _)
406+
int.random(2)
407+
|> list.contains([0, 1], _)
406408
|> should.be_true
407409

408-
int.random(-1, 2)
409-
|> list.contains([-1, 0, 1], _)
410+
int.random(3)
411+
|> list.contains([0, 1, 2], _)
410412
|> should.be_true
411413
}
412414
list.range(0, 100)
413415
|> iterator.from_list
414416
|> iterator.fold(Nil, test_boundaries)
415-
416-
let test_average = fn(iterations: Int, min: Int, max: Int, tolerance: Int) {
417-
let expected_average = int.sum([min, max]) / 2
418-
list.range(0, iterations)
419-
|> iterator.from_list
420-
|> iterator.fold(
421-
from: 0,
422-
with: fn(accumulator, _element) { accumulator + int.random(min, max) },
423-
)
424-
|> fn(sum) { sum / iterations }
425-
|> fn(average) {
426-
average - tolerance <= expected_average || average + tolerance >= expected_average
427-
}
428-
|> should.be_true
429-
}
430-
test_average(100, 0, 0, 5)
431-
test_average(1000, 0, 100, 5)
432-
test_average(1000, -100, 100, 5)
433-
test_average(1000, -100, 0, 5)
434-
test_average(1000, 0, -100, 5)
435417
}
436418

437419
pub fn divide_test() {

0 commit comments

Comments
 (0)