在F#中处理事件 [英] Working with Events in F#

查看:154
本文介绍了在F#中处理事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近问过这个问题:
在F#中重播录制的数据流并将该代码与我在这里找到的功能的一部分相结合:
http: //www.mattssoftwareblog.com/?p=271
结合如下:

  #rSystem.Reactive
#rSystem.CoreEx
#rFSharp.PowerPack
#rWindowsBase
#rPresentationCore
#rPresentationFramework
#rSystem.Xaml
#rSystem.Interactive.dll

打开系统
打开System.Linq
打开System.Collections.Generic
打开System.Net
打开System.IO
打开System.Threading
打开System.Windows
打开System.Windows.Input
打开System.Windows.Controls
打开System.Windows.Shapes
打开System.Windows.Media
打开System.Xaml
/ pre>

我需要使用这里生成的事件(来自我之前的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 {对于延迟,价格在
做! Async.Sleep(延迟)
evt.Trigger(price)}
|> Async.StartImmediate

evt.Publish.Add(printfn价格更新:%A)

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

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

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

//随机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) 。选择(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)|>忽略

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)

//主路径
let mainPath = new Path(Stroke = Brushes.Blue,StrokeThickness = 1。)
canvas.Children.Add(mainPath)

let trailingRandomsSub =(timerObs |> trailing(TimeSpan。 fromSeconds(60。)))。订阅(fun xs - > plot xs mainPath)

timer.Start()

如果将其粘贴到交互式会话中,您将看到一个蓝色线出现,随机生成,不使用我的新 evt code>事件。我想我的困惑不是从我的 evt 中了解如何制作和使用可观察。基本上,我如何使我的数据源为蓝线?



提前感谢,



Bob

解决方案

在F#中, IEvent<'T> 的IObservable<'T> 。这意味着您可以在任何可以观察到的地方使用F#事件。



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

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

F#为某些Rx提供封装功能,所以你可以使用 Observable.scan ,它有一个更多的F#-friendly语法。 Observable.add 只是订阅的另一种语法。



F#事件和可观察值之间的关键区别是在附加处理程序时可观察值开始。另一方面,当调用 StartImmediate 方法时,使用 Async.StartImmediate 创建的F#事件将立即启动这意味着 - 要使示例工作,您需要一次评估所有内容,或者写入启动事件的功能。)


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

解决方案

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

这篇关于在F#中处理事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆