F#System.Random在递归函数中做有趣的事情

时间:2016-11-07 10:47:40

标签: list recursion random f#

我有这个功能,选择4种颜色并列出它。至少我也想要它。:

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)) 

问题是虽然每次运行funktion时它会选择一种随机颜色,但它总是为整个列表选择相同的颜色。 [红色;红色;红色;红色]或[绿色;绿色;绿色;绿色]等。

每次进行递归调用时,它是如何达到相同颜色的,这是一个谜。

如果我在for循环中使用随机方法那么没问题。

有人可以向我解释这里发生了什么吗?

1 个答案:

答案 0 :(得分:4)

将您的System.Random()电话移出该功能,它将起作用。你在做什么:

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

每次递增时,您都会创建一个新的System.Random实例并将其分配给rnd。这意味着您使用的是System.Random的默认构造函数,its documentation警告:

  

...通过调用默认构造函数紧密连续创建的不同随机对象将具有相同的默认种子值,因此将生成相同的随机数集。使用单个Random对象生成所有随机数可以避免此问题。

您真正想要的是创建一个Random实例,然后重复使用其.Next()方法。一种方法是将System.Random()构造函数调用移到函数外部:

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)) 

另一种方式,如果你不想将rnd名称公开给外部代码,那就是将a变成一个嵌套在外部函数中的“内部”函数(在下面)例如,doit是外部函数):

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

这两者都应该产生你期望的真正随机(好的,伪随机)结果。