I'm new to F# and I'm trying to figure out how to return a random string value from a list/array of strings.
I have a list like this:
["win8FF40", "win10Chrome45", "win7IE11"]
How can I randomly select and return one item from the list above?
Here is my first try:
let combos = ["win8FF40";"win10Chrome45";"win7IE11"]
let getrandomitem () =
let rnd = System.Random()
fun (combos : string[]) -> combos.[rnd.Next(combos.Length)]
I wrote a blog post on exactly this topic a while ago: http://latkin.org/blog/2013/11/16/selecting-a-random-element-from-a-linked-list-3-approaches-in-f/
3 approaches are given there, with discussion of performance and tradeoffs of each.
To summarize:
Edit: I should clarify that above shows a few ways to get a random element from a list, assuming you must use a list. If it fits with the rest of your program's design, it is definitely more efficient to take a random element from an array.
Both the answers given here by latkin and mydogisbox are good, but I still want to add a third approach that I sometimes use. This approach isn't faster, but it's more flexible and more composable, and fast enough for small sequences. Depending on your needs, you can use one of higher performance options given here, or you can use the following.
Single-argument function using Random
Instead of directly enabling you to select a single element, I often define a
shuffleR
function like this:This function has the type
System.Random -> seq<'a> -> seq<'a>
, so it works with any sort of sequence: lists, arrays, collections, and lazily evaluated sequences (although not with infinite sequences).If you want a single random element from a list, you can still do that:
but you can also take, say, three randomly picked elements:
No-argument function
Sometimes, I don't care about having to pass in that
Random
value, so I instead define this alternative version:It works in the same way:
Although the purpose of
Guid.NewGuid()
isn't to provide random numbers, it's often random enough for my purposes - random, in the sense of being unpredictable.Generalised function
Neither
shuffleR
norshuffleG
are truly random. Due to the waysRandom
andGuid.NewGuid()
work, both functions may result in slightly skewed distributions. If this is a concern, you can define an even more general-purposeshuffle
function:This function has the type
(unit -> 'a) -> seq<'b> -> seq<'b> when 'a : comparison
. It can still be used withRandom
:but you can also use it with some of the cryptographically secure random number generators provided by the Base Class Library:
FSI:
Unfortunately, as you can see from this code, it's quite cumbersome and brittle. You have to know the length of the sequence up front;
RNGCryptoServiceProvider
implementsIDisposable
, so you should make sure to dispose ofrng
after use; and items will be removed fromq
after use, which means it's not reusable.Cryptographically random sort or selection
Instead, if you really need a cryptographically correct sort or selection, it'd be easier to do it like this:
Usage:
This isn't something I've ever had to do, though, but I thought I'd include it here for the sake of completeness. While I haven't measured it, it's most likely not the fastest implementation, but it should be cryptographically random.
Your problem is that you are mixing
Array
s and F#List
s (*type*[]
is a type notation forArray
). You could modify it like this to use lists:That being said, indexing into a
List
is usually a bad idea since it has O(n) performance since an F# list is basically a linked-list. You would be better off makingcombos
into an array if possible like this: