【问题标题】:Working with Events in F#在 F# 中处理事件
【发布时间】:2011-03-22 03:59:25
【问题描述】:

我最近问了这个问题: Replay Recorded Data Stream in F# 并将该代码与我在此处找到的功能子集相结合: http://www.mattssoftwareblog.com/?p=271 组合起来是这样的:

#r "System.Reactive"
#r "System.CoreEx"
#r "FSharp.PowerPack"
#r "WindowsBase"
#r "PresentationCore"
#r "PresentationFramework"
#r "System.Xaml"
#r "System.Interactive.dll"

open System
open System.Linq
open System.Collections.Generic
open System.Net
open System.IO
open System.Threading
open System.Windows
open System.Windows.Input
open System.Windows.Controls
open System.Windows.Shapes
open System.Windows.Media
open System.Xaml

我需要使用此处生成的事件(来自我之前的 SO 问题):

let prices = [ (0, 10.0); (1000, 10.5); (500, 9.5); (2500, 8.5); (500, 10.0); (1000, 10.5); (500, 9.5); (2500, 8.5) ]

let evt = new Event<float>()
async { for delay, price in prices do
          do! Async.Sleep(delay)
          evt.Trigger(price) }
        |> Async.StartImmediate

evt.Publish.Add(printfn "Price updated: %A")

用作此处随机创建的行的数据源(以下代码来自我提到的博客文章):

let create f =
    Observable.Create<_>(fun x ->
        f x
        new System.Action((fun () -> ())))

let fromEvent (event:IEvent<_,_>) = create (fun x -> event.Add x.OnNext)

// Random Walker
let rand = Random()
let mutable m = 0.
let randomWalker() =
    m <- m + (rand.NextDouble() * 10.) - 5.
    m

let timer = new System.Timers.Timer()
timer.Interval <- 100.
let timerObs = (timer.Elapsed |> fromEvent).Select(fun _ -> randomWalker())

let chartWindow = new Window(Height = 600., Width = 600.)
let canvas = new Canvas()
chartWindow.Content <- canvas
chartWindow.Show()

let line xs =
    let segs =
        seq { for x , y in xs |> List.tail ->
                LineSegment(Point(x,y), true) :> PathSegment }
    let (sx, sy) = xs |> List.head
    PathGeometry([PathFigure(Point(sx,sy), segs, false)])

let plot xs (path:Path) =
    let now = DateTime.Now
    let timeSpan = TimeSpan(0,1,0)
    let width = 600.
    let height = 600.
    let pts = xs |> List.map (fun (x:Timestamped<float>) ->
                (600.-(now - (x.Timestamp.DateTime)).TotalMilliseconds * 600. / timeSpan.TotalMilliseconds),x.Value  + 300.)
    path.Dispatcher.BeginInvoke(new SendOrPostCallback(fun pts -> path.Data <- line (pts :?> (float*float)list)), pts) |> ignore

let trailing (timespan:TimeSpan) (obs:IObservable<'

a>)  =
        obs.Timestamp()
            .Scan([], fun ys x ->
                let now = DateTime.Now
                let timespan = timespan
                x :: (ys |> List.filter (fun x -> (now - x.Timestamp.DateTime) < timespan)))
            .Where(fun xs -> xs |> List.length > 1)

    // Main Path
    let mainPath = new Path(Stroke=Brushes.Blue, StrokeThickness=1.)
    canvas.Children.Add(mainPath)

    let trailingRandomsSub = (timerObs |> trailing (TimeSpan.FromSeconds(60.))).Subscribe(fun xs -> plot xs mainPath)

    timer.Start()

如果您将其粘贴到交互式会话中,您将看到一条蓝线出现,它是随机生成的,而不是使用我的新 evt Event。我想我的困惑是不了解如何从我的evt 制作和使用Observable。基本上,我怎样才能让 evt 成为我的蓝线数据源?

提前致谢,

鲍勃

【问题讨论】:

    标签: wpf events f# system.reactive


    【解决方案1】:

    在 F# 中,IEvent&lt;'T&gt; 接口继承自 IObservable&lt;'T&gt;。这意味着您可以在任何需要观察到的地方使用 F# 事件。

    您的应用程序的最后一点(获取事件、添加时间戳、使用Scan 获取包含到目前为止生成的项目的列表并绘制进度)可以这样编写:

    let trailingRandomsSub = 
      evt.Publish.Timestamp()
      |> Observable.scan (fun l e -> e::l) []
      |> Observable.add (fun xs -> plot xs mainPath)
    

    F# 为某些 Rx 函数提供了包装器,因此您可以使用 Observable.scan,它具有更适合 F# 的语法。 Observable.add 只是 Subscribe 的另一种语法。

    F# 事件和可观察对象之间的主要区别在于,可观察对象在您附加处理程序时启动。另一方面,您使用 Async.StartImmediate 创建的 F# 事件会在调用 StartImmediate 方法时立即启动(这意味着 - 要使示例正常工作,您需要立即评估所有内容,或者编写一个函数来启动事件)。

    【讨论】:

    • 实际上,我怀疑是这种情况并尝试了我能想到的所有替换/重构操作,但根本无法弄清楚如何使这两个代码 sn-ps 一起工作。我怀疑计时器可能是使集成变得困难的原因,但不知道究竟是什么。
    • @Beaker:我在玩你的代码,并添加了一个示例来展示如何在你的场景中进行设置。
    • 我想我现在明白我做错了什么。你到底改了什么?
    • 您的意思是您提供的功能,我以为您的意思是您以某种方式更改了我的代码。我想我需要找到一个很好的信息来源来学习如何将 Rx 与 F# 一起使用。再次感谢托马斯!
    • @Beaker:是的,答案中的代码就是您所需要的。在我的 F# 书 (manning.com/petricek) 的第 16 章中有一个关于 F#、Rx 中的事件和使用异步进行 GUI 编程的简短介绍。你可以看看源代码 (archive.msdn.microsoft.com/realworldfp) 看看有没有你感兴趣的...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-01
    • 2014-09-29
    • 2020-09-10
    相关资源
    最近更新 更多