F#中的随机数组元素

Jos*_*lin 1 f#

我试图在F#上学习自己,我担心我不理解我应该做的事情.

我正在尝试重新创建一本我喜欢的书的功能(来自Royal和Panarese的Creative Cursing).

简而言之,您有两个单独的单词列表,可以从中选择两个随机单词,从而产生一个奇怪的短语.够简单吗?

这就是我所拥有的:


#light

open System
open System.IO

let getWordList file =
  File.ReadAllLines( file )

let getRandArrElement (arr : string[]) =
  let rnd = Random( 0 )
  arr |> Seq.nth (rnd.Next arr.Length)

let wordList1 = getWordList "words1.txt"
let wordList2 = getWordList "words2.txt"

let word1 = getRandArrElement wordList1
let word2 = getRandArrElement wordList2

printf "%s %s" word1 word2
Run Code Online (Sandbox Code Playgroud)

它也有效.除了它每次运行时返回相同的短语.

我有一种感觉,它正在做的是在编译时每次调用"getRandArrElement"计算一个随机值,然后使用该值作为THE值(我认为这很奇怪,但我知道什么?).

我的逻辑有什么问题,我该如何解决?

Jul*_*iet 10

你的问题在这里:

let getRandArrElement (arr : string[]) =
  let rnd = Random( 0 )
  arr |> Seq.nth (rnd.Next arr.Length
Run Code Online (Sandbox Code Playgroud)

Random数字并不是真正随机的.他们取一个种子值,在0.0和之间计算一个随机数1.0; 新值用作下一个种子.换句话说,Random我确切地说是确定性的,所以反复播种相同的值会产生相同的输出序列.

而且由于你总是构造一个具有相同种子的新Random,你每次都得到与输出相同的随机数.

我建议一些改进:

  • let rnd = Random().默认构造函数使用系统时钟作为种子,因此您将获得不同的序列.(它仍然可以获得相同的序列.系统时钟具有大约10毫秒的分辨率,因此在该间隔中构造两个Random将很可能以相同的值播种.

    如果您使用let rnd = Random(0),即使rnd不在您的功能范围内,每次运行程序时,您都会以完全相同的顺序获得相同的句子.

  • 您可以移动rnd函数外部的声明,这样您就不会一遍又一遍地构造它.作为替代方案,您可以这样写:

    let getRandArrElement =
        let rnd = Random()
        fun (arr : string[]) -> ...
    
    Run Code Online (Sandbox Code Playgroud)

    打开模块时,F#执行所有无参数值,因此rnd将立即分配,并getRandArrElement赋值为fun (arr : string[]) -> ....

  • arr.[index]而不是arr |> Seq.nth (rnd.Next arr.Length).它不仅更简洁,而且还是O(1).Seq.nth将它视为一个序列,它一次遍历一个元素,直到它到达与给定索引匹配的元素,从而进行操作O(n).

最终结果应该是这样的:

let getRandArrElement =
  let rnd = Random()
  fun (arr : string[]) -> arr.[rnd.Next(arr.Length)]
Run Code Online (Sandbox Code Playgroud)