在F#中处理事件 [英] Working with Events in F#
问题描述
在F#中重播录制的数据流并将该代码与我在这里找到的功能的一部分相结合:
http: //www.mattssoftwareblog.com/?p=271
结合如下:
#rSystem.Reactive
/ pre>
#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
我需要使用这里生成的事件(来自我之前的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 anObservable
from myevt
. Basically, how can I make evt my data source for the blue line?Thanks in advance,
Bob
解决方案In F#, the
IEvent<'T>
interface inherits fromIObservable<'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 forSubscribe
.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 theStartImmediate
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屋!