Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 为什么在使用Observable.merge时顺序很重要?_F#_System.reactive - Fatal编程技术网

F# 为什么在使用Observable.merge时顺序很重要?

F# 为什么在使用Observable.merge时顺序很重要?,f#,system.reactive,F#,System.reactive,我试图用F#中的可观测值编写一个基本的“游戏循环”。基本上,我将事件的基本输入流概念化为两个流合并在一起:用户的按键(游戏开始时只使用键盘)和游戏的常规滴答声(比如,每秒60次) 我的问题似乎源于这样一个事实,即观察到的序列之一,即滴答声,也是在窗口上调用DispatchEvents()的循环,允许它处理其输入并触发按键事件,因此一个事件流实际上是由另一个事件流驱动的,如果这有意义的话。代码如下: open System; open System.IO open SFML.Window open

我试图用F#中的可观测值编写一个基本的“游戏循环”。基本上,我将事件的基本输入流概念化为两个流合并在一起:用户的按键(游戏开始时只使用键盘)和游戏的常规滴答声(比如,每秒60次)

我的问题似乎源于这样一个事实,即观察到的序列之一,即滴答声,也是在窗口上调用DispatchEvents()的循环,允许它处理其输入并触发按键事件,因此一个事件流实际上是由另一个事件流驱动的,如果这有意义的话。代码如下:

open System;
open System.IO
open SFML.Window
open SFML.Graphics
open System.Reactive
open System.Reactive.Linq
open System.Diagnostics

type InputEvent =
| Tick of TimeSpan
| KeyPressed of Keyboard.Key

[<EntryPoint;STAThread>]
let main _ = 

    use window = new RenderWindow(VideoMode(640u, 480u), "GameWindow")
    window.SetVerticalSyncEnabled(true)

    let displayStream = 
        Observable.Create(
            fun (observer:IObserver<TimeSpan>) -> 
                let sw = Stopwatch.StartNew()
                while (window.IsOpen()) do
                    window.DispatchEvents() // this calls the KeyPressed event synchronously
                    window.Display() // this blocks until the next vertical sync
                    window.Clear()
                    observer.OnNext sw.Elapsed
                    sw.Restart()
                observer.OnCompleted();
                { new IDisposable with member this.Dispose() = ()})    

    let onDisplay elapsedTime = 
        // draw game: code elided

    let inputEvents = Observable.merge 
                          (window.KeyPressed |> Observable.map (fun key -> KeyPressed(key.Code)))
                          (displayStream |> Observable.map (fun t -> Tick(t)))
    use subscription = 
        inputEvents.Subscribe(fun inputEvent -> match inputEvent with
                                                | Tick(t) -> onDisplay(t)
                                                | KeyPressed(key) -> printfn "%A" key)

    0
然后游戏渲染(调用onDisplay),但我看不到打印到控制台的按键事件。为什么呢


(如果您想知道什么是SFML,下面是示例)。

在伪代码中,merge的作用是:

firstStream.Subscribe(...);
secondStream.Subscribe(...);
传递给
Observable.create
的subscribe函数是同步的,不会将控制权返回给调用方。这意味着
merge
本身被阻止尝试订阅
displayStream
之后的任何流。当您对流进行重新排序,使
displayStream
位于第一位时,您会阻止它订阅您的
按键的
流。这就是为什么你会看到你所看到的行为

在某些方面,您的
displayStream
表现不好<代码>订阅
方法不应被阻止


因此,要么确保
displayStream
是列表中的最后一项,要么对代码进行重构。对于
displayStream
,您可以只使用
主题
。然后订阅所有内容,最后启动“显示循环”,在这里执行当前位于
displayStream
定义中的循环,每次通过循环时,只需调用
OnNext

+1-观察效果很好-我没有注意到其中的阻塞循环:(
firstStream.Subscribe(...);
secondStream.Subscribe(...);