我试图在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
也有效。除了它每次运行时返回相同的短语。
我感觉它正在做的是在编译时每次调用“getRandArrElement”计算一个随机值,然后使用该值作为THE值(我认为这很奇怪,但我知道什么?)。
我的逻辑有什么问题,我该如何解决?
答案 0 :(得分:10)
你的问题在这里:
let getRandArrElement (arr : string[]) =
let rnd = Random( 0 )
arr |> Seq.nth (rnd.Next arr.Length
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[]) -> ...
当您打开模块时,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)]
答案 1 :(得分:3)
每次使用具有相同种子的新Random,这是预期的行为 - 如果重复使用相同的种子,则生成相同的数字序列。我建议你将rnd的声明移出函数,这将解决你的问题:
let rnd = Random();
let getRandArrElement (arr : string[]) =
arr |> Seq.nth (rnd.Next arr.Length)