From FizzBuzz to FizzBuzzBizzFuzz

The orginal idea has been applied to F# as an example to learn reactive observables.

kata_fizzbuzz

I prefer `TimeSpan` instead of `DateTime` for timers 😉


type FizzBuzzEvent = {label:int; time: TimeSpan}

let createTimerAndObservable timerInterval =
    // setup a timer
    let timer = new System.Timers.Timer(float timerInterval)
    timer.AutoReset <- true

    // events are automatically IObservable
    let observable = timer.Elapsed  

    // return an async task
    let task = async {
        timer.Start()
        do! Async.Sleep 120000
        timer.Stop()
        }

    // return a async task and the observable
    (task,observable)

The math

You need to know that primes number are central glue when you want to factorize everything: including time itself, and therefore time series…

[<EntryPoint>]
let main argv =
    // create the event streams and raw observables
    let timer3, timerEventStream3 = createTimerAndObservable 300
    let timer5, timerEventStream5 = createTimerAndObservable 500
    let timer7, timerEventStream7 = createTimerAndObservable 700
    let timer11, timerEventStream11 = createTimerAndObservable 1100

    let start = DateTime.Now
    // convert the time events into FizzBuzz events with the appropriate id
    let eventStream3  = timerEventStream3
                        |> Observable.map (fun _ -> {label=3; time=DateTime.Now-start})
    let eventStream5  = timerEventStream5
                        |> Observable.map (fun _ -> {label=5; time=DateTime.Now-start})
    let eventStream7  = timerEventStream7
                        |> Observable.map (fun _ -> {label=7; time=DateTime.Now-start})
    let eventStream11  = timerEventStream11  
                        |> Observable.map (fun _ -> {label=11; time=DateTime.Now-start})

    // combine the two streams
    let combinedStream = 
       Observable.merge eventStream3 eventStream5
       |> Observable.merge eventStream7
       |> Observable.merge eventStream11

    let nameEv   = function
        | 0 -> "starting..."
        | 3 -> "Fizz"
        | 5 -> "Buzz"
        | 7 -> "Bizz"
        | 11 -> "Fuzz"
        | e -> "Error " + e.ToString()

From pairwise to multiwise

    // make pairs of events
    let multiwiseStream =
       combinedStream // |> Observable.pairwise
       // http://disq.us/p/jr14pn https://fsharpforfunandprofit.com/posts/concurrency-reactive/#comment-1194259739
       |> Observable.scan (fun (f, s, t, r) n -> (s, t, r, n))
            ({label=0; time=TimeSpan()}, {label=0; time=TimeSpan()},
                {label=0; time=TimeSpan()}, {label=0; time=TimeSpan()})
    // split the stream based on whether the pairs are simultaneous
    let simultaneous4Stream, nonSimultaneousStream4 =
       multiwiseStream |> Observable.partition areSimultaneous4
    let simultaneous3Stream, nonSimultaneousStream3 =
       nonSimultaneousStream4 |> Observable.partition areSimultaneous3
    let simultaneous2Stream, nonSimultaneousStream =
       nonSimultaneousStream3 |> Observable.partition areSimultaneous2

It’s like converting binary numbers to a different basis, in this context `Observable.scan` is replacing `Observable.pairwise`

let areSimultaneous4 (earlierEvent1,earlierEvent2,laterEvent1,laterEvent2) =
    let {label=_;time=t1} = earlierEvent1
    let {label=_;time=t2} = laterEvent2
    t2.Subtract(t1).Milliseconds < 50

let areSimultaneous3 (earlierEvent1,earlierEvent2,laterEvent1,laterEvent2) =
    let {label=_;time=t1} = earlierEvent2
    let {label=_;time=t2} = laterEvent2
    t2.Subtract(t1).Milliseconds < 50

let areSimultaneous2 (earlierEvent1,earlierEvent2,laterEvent1,laterEvent2) =
    let {label=_;time=t1} = laterEvent1
    let {label=_;time=t2} = laterEvent2
    t2.Subtract(t1).Milliseconds < 50

Mapping is subscribing

We change languages, idioms, but the underlying logic is always the same, a `map` for `Enumerable` becomes a `subscribe` for `Observable`.

    //print events from the combinedStream
    combinedStream 
    |> Observable.subscribe (fun {label=id;time=t} -> 
                                  printfn "[%i] %02i.%02i.%03i " 
                                    id t.Minutes t.Seconds t.Milliseconds)
 
    //print events from the simultaneous stream
    simultaneous4Stream 
    |> Observable.subscribe (fun _ -> printfn "FizzBuzzBizzFuzz")


    simultaneous3Stream 
    |> Observable.subscribe (fun (_,ev1,ev2,ev3) -> 
            printfn "%s%s%s" 
                (nameEv ev1.label) (nameEv ev2.label) (nameEv ev3.label))
    
    simultaneous2Stream 
    |> Observable.subscribe (fun (_,_,ev1,ev2) -> 
            printfn "%s%s" 
                (nameEv ev1.label) (nameEv ev2.label))

    //print events from the nonSimultaneous streams
    nonSimultaneousStream 
    |> Observable.subscribe (fun (_,_,_,ev) -> printfn "%s" <| nameEv ev.label)

    // run the two timers at the same time
    [timer3;timer5;timer7;timer11]
    |> Async.Parallel
    |> Async.RunSynchronously

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s