出于学习目的,我正在尝试使用F#运行模拟作为序列。从一系列随机数开始,如果状态不依赖于先前的状态,则map是生成状态序列的直接方式。我遇到问题的地方是当我尝试做类似的事情时:
状态(i + 1)= F(状态(i),随机数)
我设法通过使用展开来获得一些工作,沿着
let unfold (day:State,rnd:Random) =
let rand = rnd.NextDouble()
let nextDay = NextState day rand
Some (nextDay, (nextDay, rnd))
然而,至少对于我没有经验的眼睛来说,关于绕过Random实例的事情似乎很可疑。有没有办法实现类似的东西,但传递一系列随机数,而不是生成器?
答案 0 :(得分:4)
我同意BrokenGlass的观点,即在这种情况下使用全局Random
实例感觉很好。这是对可变状态的合理本地化使用,所以不应该混淆。
作为unfold
的替代方案,您可以考虑明确地编写计算:
let simulationStates =
let rnd = new Random()
let rec generate (day:State) = seq {
let rand = rnd.NextDouble()
let nextDay = NextState day rand
yield nextDay
yield! generate nextDay }
generate InitialState
请注意,rnd
值是局部变量,其范围仅限于simulationStates
的定义。这是将可变状态与程序其余部分分开的非常好的方法。
使用unfold
的版本可能更简洁;这个可能更容易阅读,所以这取决于你的个人风格偏好。
答案 1 :(得分:3)
我认为你认为将Random
实例传递为鱼腥的预感是公平的:当可变状态有用时,隔离它是一个好主意,这样你就可以尽可能地获得纯度。
我们可以通过创建一个序列来隔离这里的状态,该序列在每次迭代时产生不同的随机数集
open System
let rndSeq =
seq {
//note that by putting rnd inside seq expression here, we ensure that each iteration of the sequence
//yields a different sequnce of random numbers
let rnd = new Random()
while true do yield rnd.NextDouble()
}
然后,您可以使用Seq.scan
通过使用由映射的前一个元素通知的函数映射元素来迭代随机序列。
let runSimulation inputSeq initialState =
inputSeq
|> Seq.scan
(fun (previousState:State) (inputElement:float) -> NextState previousState inputElement)
initialState
runSimulation rndSeq initialState //run the simulation using a random sequence of doubles greater than or equal to 0.0 and less than 1
您可以在此看到,您的模拟输入和模拟实现不再绑定在一起,您可以使用任何输入序列运行模拟。
答案 2 :(得分:2)
可能违背精神,但在这种情况下我只会使用一个全局的Random实例 - 或者你可以像这样定义一系列随机数:
let randomNumbers =
seq {
let rnd = new Random();
while true do
yield rnd.NextDouble();
}