在 F# 中处理事件

发布于 2024-10-25 20:40:59 字数 3359 浏览 1 评论 0原文

我最近问了这个问题: 在 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 成为蓝线的数据源?

预先感谢,

鲍勃

I recently asked this question:
Replay Recorded Data Stream in F# and combined that code with a subset of the functionality I found here:
http://www.mattssoftwareblog.com/?p=271
which combined looks like this:

#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

I need to use the events generated here (which came from my earlier SO question):

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")

to use as the data source for the line which is randomly created here (the code below comes from the blog article I mentioned):

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()

If you paste this into an interactive session you will see a blue line emerge which is generated randomly and not using my new evt Event. I guess my confusion is not understanding how to make and use an Observable from my evt. Basically, how can I make evt my data source for the blue line?

Thanks in advance,

Bob

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

做个少女永远怀春 2024-11-01 20:41:19

在 F# 中,IEvent<'T> 接口继承自 IObservable<'T>。这意味着您可以在任何需要可观察值的地方使用 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 方法时立即启动(这意味着 - 要使示例正常工作,您需要立即评估所有内容,或编写一个启动事件的函数)。

In F#, the IEvent<'T> interface inherits from IObservable<'T>. This means that you can use F# events in any place where an observable is expected.

The last bit of your application (that takes the event, adds time stamps, uses Scan to get lists containing the items generated so far and plots the progress) can be written like this:

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

F# provides wrappers for some of the Rx functions, so you can use Observable.scan, which has a bit more F#-friendly syntax. Observable.add is just another syntax for Subscribe.

The key difference between F# events and observables is that observables start when you attach a handler. On the other hand, the F# event that you create using Async.StartImmediate starts immediately when the StartImmediate method is called (this means - to get the sample working, you need to evaluate everything at once, or write a function that starts the event).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文