This is the first post in our series about F# 9! Keep your eyes out for more posts highlighting shiny new F# features!
A while ago, I wrote this blog post, about taking a random sample out of a list in F#. What I did not know at that time, was that a more permanent solution to that problem had already been introduced in the F# Core library. Now, with the release of F# 9, we have a whole new array of functions to get random items from a collection!
Array.randomSample
Let's start with the issue we encountered in my previous blog post. We wanted to get a random, non-repeating sample from a list. We drew the conclusion that System.Random.GetItems
, was not very useful for that, as it allows for repetition. We came up with the following code to solve the issue:
let shuffled array =
let copy = Array.copy array
Random.Shared.Shuffle copy
copy
let takeRandom array n =
array
|> shuffled
|> Array.take n
With F# 9, we now have the Array.randomSample
function (there is also a List
and Seq
equivalent), that returns N non-repeating samples:
let sample =
[|"Apple"; "Pear"; "Orange"; "Melon"|]
|> Array.randomSample 3
// [|"orange"; "Melon"; "Apple"|]
Simple as that! Do note that there are a few failure modes here; the function throws if:
- The input list is empty (even if you take 0 samples!)
- The input list contains fewer items than items requested
- Less than 0 items are requested
Other random operations
Apart from randomSample
, there is also:
randomChoice source
, for getting a single random value:let sample = [|"Apple"; "Pear"; "Orange"; "Melon"|] |> Array.randomChoice // "Apple"
randomChoices count source
, for getting multiple values, allowing repetition:let sample = [|"Apple"; "Pear"; "Orange"; "Melon"|] |> Array.randomChoices 7 // [|"Melon"; "Pear"; "Orange"; "Pear"; "Orange"; "Apple"; "Apple"|]
randomShuffle source
, to re-order the values:let sample = [|"Apple"; "Pear"; "Orange"; "Melon"|] |> Array.randomShuffle // [|"Melon"; "Orange"; "Pear"; "Apple"|]
Random sources
You are also very flexible with the source of random you use! All these functions have 3 permutations, each allowing for another source of randomness. The following suffixes are available:
- no suffix: uses a thread safe random number generator under the hood
...By
: takes a function of unit that returns a float between 0.0 and 1.0 as a source of randomness. You could for example use this to create predictable test resultslet sample = [|"Apple"; "Pear"; "Orange"; "Melon"|] |> Array.randomSampleBy (fun () -> 0.5) 3 // [|"Orange"; "Pear"; "Melon"|]
...With
: Takes an instance ofSystem.Random
as a source of randomness:let random = System.Random() let sample = [|"Apple"; "Pear"; "Orange"; "Melon"|] |> Array.randomSampleWith random 3 // [|"Orange"; "Apple"; "Pear"|]
These new functions make working with random values in F# a breeze! Keep an eye out on the blog for more highlights of new F# features, such as nullable reference types, and auto-generated properties on DU cases.