Работа с событиями в F # - PullRequest
       30

Работа с событиями в F #

4 голосов
/ 22 марта 2011

Я недавно задал этот вопрос: Воспроизведение записанного потока данных в 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.Я предполагаю, что мое замешательство не в понимании того, как сделать и использовать Observable из моего evt.В основном, как я могу заставить evt мой источник данных для синей линии?

Заранее спасибо,

Bob

1 Ответ

5 голосов
/ 22 марта 2011

В 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 # и наблюдаемыми заключается в том, что наблюдаемые запускаются, когда вы присоединяете обработчик.С другой стороны, событие F #, которое вы создаете с помощью Async.StartImmediate, начинается сразу же после вызова метода StartImmediate (это означает, что для того, чтобы образец работал, вам нужно все оценить сразу или написать функцию, которая запускаетсобытие).

...