F# 滞后或标准偏差的自定义运算符

F# 滞后或标准偏差的自定义运算符,f#,system.reactive,F#,System.reactive,使用RX时,扩展可用操作员的正确方法是什么 我想建立一些我认为有用的操作 第一个操作就是一系列的标准偏差 第二个操作是第n个滞后值,即,如果我们滞后2,并且我们的系列是B C D e F,当F被推动时,滞后将是D,当A被推动时,滞后将是空的,当B被推动时,滞后将是空的,当C被推动时,滞后将是A 将这些类型的操作员从rx.codeplex.com的内置程序中分离出来是否有意义,或者是否有更简单的方法?其中一些操作比其他操作更简单(通常如此)。对于计数的“滞后”(而不是时间),您只需使用与“滞后”大

使用RX时,扩展可用操作员的正确方法是什么

我想建立一些我认为有用的操作

第一个操作就是一系列的标准偏差

第二个操作是第n个滞后值,即,如果我们滞后2,并且我们的系列是B C D e F,当F被推动时,滞后将是D,当A被推动时,滞后将是空的,当B被推动时,滞后将是空的,当C被推动时,滞后将是A


将这些类型的操作员从rx.codeplex.com的内置程序中分离出来是否有意义,或者是否有更简单的方法?

其中一些操作比其他操作更简单(通常如此)。对于计数的“滞后”(而不是时间),您只需使用与“滞后”大小相等的Observable.Buffer创建一个滑动窗口,然后获取结果列表的第一个元素

到目前为止,滞后=3,函数为:

obs.Buffer(3,1).Select(l => l.[0])
将其转换为扩展函数非常简单。我不知道它是否有效,因为它重用了相同的列表,但在大多数情况下,这并不重要。我知道你想要F#,翻译很简单

对于正在运行的聚合,通常可以使用
Observable.Scan
获取“正在运行”的值。这是根据迄今为止看到的所有值计算出来的(实现起来非常简单)——即您只需实现每个后续元素,即之前的聚合和新元素

如果出于任何原因,您需要一个基于滑动窗口的运行聚合,那么我们将进入更困难的领域。在这里,您首先需要一个操作,可以给您一个滑动窗口-这是由上面的缓冲区涵盖。但是,您需要知道哪些值已从此窗口中删除,哪些已添加

因此,我建议使用一个新的可观察函数,该函数基于现有窗口+新值维护一个内部窗口,并返回新窗口+移除值+增加值。您可以使用Observable.Scan编写此代码(我建议使用内部队列以实现高效实现)。它应该使用一个函数来确定给定一个新值要删除哪些值(通过这种方式,它可以通过时间或计数进行参数化)

此时,Observable.Scan可再次用于获取旧聚合+窗口+移除值+附加值,并给出新聚合


希望这能有所帮助,我确实意识到这是一大堆话。如果您可以确认需求,我可以为特定用例提供实际的扩展方法。

对于
lag
,您可以执行以下操作

module Observable =
  let lag n obs =
    let buf = System.Collections.Generic.Queue()
    obs |> Observable.map (fun x ->
      buf.Enqueue(x)
      if buf.Count > n then Some(buf.Dequeue())
      else None)
这:

印刷品:

<null>
<null>
Some 1
Some 2
Some 3
Some 4
Some 5
Some 6
Some 7

大约1
大约2
大约3
大约4
大约5
大约6
大约7

在惯用Rx中,任意延迟可以由
Zip
组成

let lag (count : int) o = 
    let someo = Observable.map Some o
    let delayed = Observable.Repeat(None, count).Concat(someo)        
    Observable.Zip(someo, delayed, (fun c d -> d))    
对于滚动缓冲区,最有效的方法是简单地使用固定大小的
队列
/
大小调整数组

let rollingBuffer (size : int) o = 
    Observable.Create(fun (observer : IObserver<_>) -> 
    let buffer = new Queue<_>(size)
    o |> Observable.subscribe(fun v -> 
            buffer.Enqueue(v)
            if buffer.Count = size then
                observer.OnNext(buffer.ToArray())
                buffer.Dequeue() |> ignore
        )
    )
对于相邻值的配对,只需使用
Observable.pairwise

let delta (a, b) = b - a
let deltaStream = numbers |>  Observable.pairwise |> Observable.map(delta) 

Observable.Scan
如果要应用滚动计算,则更简洁。

是否要运行stdev?如果没有,您可以使用
Observable.Aggregate
。将同时需要运行和非运行。组成管道时,可以多次重复应用map/select。地图操作员有副作用不是一个好主意。你能给我一个例子说明这不起作用吗?“副作用”是自包含的,而不是副作用通常存在的“副作用”。问题是当
map
被重新应用时,您的原始功能不会被应用。
Buffer
在缓冲所有项目之前不会推送。OP想要在推送第一个项目时返回
None
。在这方面
ResizeArray
Queue
更好吗?@Daniel你说得对。但是
Queue
似乎会干扰类型推断。
seq [0L; 1L; 2L]
seq [1L; 2L; 3L]
seq [2L; 3L; 4L]
seq [3L; 4L; 5L]
...
let delta (a, b) = b - a
let deltaStream = numbers |>  Observable.pairwise |> Observable.map(delta)