从序列的前一个元素和另一个序列构建的序列

时间:2011-04-10 22:36:20

标签: f# sequence

出于学习目的,我正在尝试使用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实例的事情似乎很可疑。有没有办法实现类似的东西,但传递一系列随机数,而不是生成器?

3 个答案:

答案 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();
         }