使用无限序列进行压缩,然后始终为false

时间:2016-11-22 11:25:05

标签: c# system.reactive infinite

我做了一个扩展方法:

public static IObservable<T> RateLimit<T>(this IObservable<T> source, 
                                          TimeSpan minDelay)
{
    return
        source.TimeInterval()
            .Select(
                (x, i) =>
                    Observable.Return(x.Value)
                        .Delay(i == 0
                            ? TimeSpan.Zero
                            : TimeSpan.FromTicks(
                                  Math.Max(minDelay.Ticks - x.Interval.Ticks, 0))))
            .Concat();
}

这会创建一个新的observable,它只允许项目以最小的时间间隔通过。

要消除初始延迟,必须以不同方式处理第一项。

可以看出,有一个测试,看我们是否通过测试i == 0来处理第一个项目。这里的问题是,如果我们处理的项目超过int.MaxValue,则会失败。

相反,我想到了以下序列

var trueThenFalse = Observable.Return(true)
                    .Concat(Observable.Repeat(false))

并将其与我的来源一起压缩:

source.TimeInterval().Zip(trueThenFalse, ...

但是当将这个无限序列传递给Zip时,我们似乎进入了一个紧密的循环,其中trueThenFalse一次性(向无穷大)发出所有项目。失败。

我可以通过副作用(例如外部范围中的bool)轻松编码,但这可能代表了我不满意的纯度损失。

有什么建议吗?

修改

虽然行为不完全相同,但以下代码表现出一些讨厌的特征

var trueThenFalse = Observable.Return(true)
    .Concat(Observable.Repeat(false));
var src = Observable.Interval(TimeSpan.FromSeconds(1)); //never completes
src.Zip(trueThenFalse,(i,tf)=>tf).ForEach(x=>Trace.TraceInformation("{0}",x));

最终死于OOME。这是因为trueThenFalse似乎取消了它的所有值,但Zip并没有及时使用它们。

2 个答案:

答案 0 :(得分:3)

事实证明,Zip有another overload可以将IObservable序列与IEnumerable序列压缩在一起。

通过将IObservable的推送语义与IEnumerable的pull语义相结合,可以使我的测试用例工作。

所以,使用以下方法:

private IEnumerable<T> Inf<T>(T item)
{
    for (;;)
    {
        yield return item;
    }
}

我们可以创建一个IEnumerable:

var trueThenFalse = Enumerable.Repeat(true, 1).Concat(Inf(false));

然后用源可观察源拉链:

var src = Observable.Interval(TimeSpan.FromSeconds(1));
src.Zip(trueThenFalse, (i, tf) => tf).ForEach(x => Trace.TraceInformation("{0}", x));

......一切都按预期工作。

我现在对RateLimiter方法有以下实现:

public static IObservable<T> RateLimit<T>(this IObservable<T> source,
                                          TimeSpan minDelay)
{
    var trueThenFalse = Enumerable.Repeat(true, 1).Concat(Inf(false));
    return
        source.TimeInterval()
            .Zip(trueThenFalse, (item, firstTime) => Observable.Return(item.Value)
                .Delay(firstTime
                    ? TimeSpan.Zero
                    : TimeSpan.FromTicks(
                        Math.Max(minDelay.Ticks - item.Interval.Ticks, 0))))

            .Concat();
}

答案 1 :(得分:1)

这类似于Rx IObservable buffering to smooth out bursts of events,但您显然正在尝试理解为什么您的解决方案有效/无效。

我发现那里的解决方案更优雅,但每个人都有。