I am trying to use F# to generate a list of random triples -> two random numbers and their sum:
let foo minNum maxNum =
let rnd = System.Random()
let first = rnd.Next(minNum, maxNum)
let second = rnd.Next(minNum, maxNum)
let third = first + second
first, second, third
which can be called like this and works well (always gives a new random number) (when using F# interactive)
foo 0 50
When trying to generate a list of random triples like this
List.init 100 (fun index -> foo 0 50)
I would like this to be a list of 100 randomized triples, but instead they all come out with the same value. I can see that the function doesn't depend on the index and therefore does not need to recalculate, but I am not sure how to work around it (I tried introducing the index as an unused dummy variable, also tried introducing the index as a random seed, but neither helped)
You're assuming that the reason that your code does not work like you want it to is that the compiler erroneously applies some sort of common subexpression elimination, which causes foo 0 50
to be evaluated only once. This is not the case. The compiler knows very well that functions may be impure and won't perform optimizations that would break with impure code (unless the specific code that is being optimized can be proven to be pure). So you don't have to worry about making the compiler think that you're using the argument.
The problem isn't that foo 0 50
is called only once - it is indeed called 100 times like you want. The problem is that it returns the same value each of the 100 times it is called.
But why would it do that when it returns different values when you test it manually?
Because you create a new Random object every time you call foo
. Since you don't provide a seed value to that random object, it will be seeded with the current system time. So if you call the function multiple times with some time passing in between, it will return different values each time. However if you call it 100 times in such a short amount of time that the system time does not change in between, you'll get back the same value 100 times.
So the solution to your problem is to reuse the same random object, instead of creating a new one each time foo
is called.
Try this instead. It will reuse the same random object without you having to keep one around in your main scope:
let foo =
let rnd = System.Random()
fun minNum maxNum ->
let first = rnd.Next(minNum, maxNum)
let second = rnd.Next(minNum, maxNum)
let third = first + second
first, second, third
Also note that Next is not threadsafe so if you intend to use foo from several threads you need to add some locking to Next.
To complement sepp2k answer with some code:
let rnd = System.Random()
let foo minNum maxNum =
let first = rnd.Next(minNum, maxNum)
let second = rnd.Next(minNum, maxNum)
let third = first + second
first, second, third
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With