简体   繁体   中英

F# System.Random in a Recursive function doing funny stuff

I have this funktion that chooses 4 randoms colors and makes a list of it. Atleast i want it too.:

let theList = [Red;Green;Yellow;Purple;White;Black]
let rec a x =
  let rnd = System.Random()
  match x with 
  |0 -> []
  |_ -> (List.item(rnd.Next(6)) theList)::(a (x-1)) 

The problem is though it picks a random color every time i run the funktion then it always picks the same color for the entire list. [Red;Red;Red;Red] or [Green;Green;Green;Green] etc.

It is a mystery to me how it can arrive at the same color everytime it makes the recursive call.

If i use the random method in a for loop then no problem.

Can someone explain to me what is happening here?

Move your System.Random() call out of the function and it will work. What you're doing is:

let rec a x =
    let rnd = System.Random()
    // ... Some code that calls rnd.Next() once, then recurses

Every time you recurse, you're creating a new System.Random instance and assigning it to rnd . That means that you're using the default constructor of System.Random , and its documentation warns that:

... different Random objects that are created in close succession by a call to the default constructor will have identical default seed values and, therefore, will produce identical sets of random numbers. This problem can be avoided by using a single Random object to generate all random numbers.

What you really want is to create a single Random instance, then use its .Next() method repeatedly. One way would be to move the System.Random() constructor call outside of the function:

let theList = [Red;Green;Yellow;Purple;White;Black]
let rnd = System.Random()
let rec a x =    
    match x with 
    |0 -> []
    |_ -> (List.item(rnd.Next(6)) theList)::(a (x-1)) 

Another way, if you don't want to expose the rnd name to outside code, would be to turn a into an "inner" function that's nested inside an outer function (in the following example, doit is the outer function):

let theList = [Red;Green;Yellow;Purple;White;Black]
let doit x =
    let rnd = System.Random()
    let rec a x =
        match x with 
        |0 -> []
        |_ -> (List.item(rnd.Next(6)) theList)::(a (x-1))
    a x

Both of these should produce the truly random (well, pseudo-random) results that you're expecting.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM