C# 学习Rx:如何使用.Window的输出扫描可观察的布尔值序列
我有一系列真假值,就像这样C# 学习Rx:如何使用.Window的输出扫描可观察的布尔值序列,c#,system.reactive,C#,System.reactive,我有一系列真假值,就像这样 var alternatingTrueFalse = Observable.Generate( true, _ => true, x => !x, x => x, _ => TimeSpan.FromMilliseconds(new Random().Next(2000))) .Take
var alternatingTrueFalse = Observable.Generate(
true,
_ => true,
x => !x,
x => x,
_ => TimeSpan.FromMilliseconds(new Random().Next(2000)))
.Take(20).Publish();
alternatingTrueFalse.Connect();
var buffered = alternatingTrueFalse
.Buffer(TimeSpan.FromMilliseconds(500))
.Subscribe(x => Console.WriteLine($"{TimeStamp} {string.Join(",", x)}"));
我想看看500毫秒(最大)窗口/缓冲区的顺序。若在一个这样的窗口中只有一个真值(并没有其他值),我想翻转一个开关(只需调用一个命令,现在打印到控制台)。然后,当下一个假值到达时,我想将开关翻转回来,关闭原始序列的当前窗口/缓冲区,然后启动一个新的窗口/缓冲区
使用缓冲+扫描翻转开关
到目前为止,我已经找到了一种在缓冲区上实现这一点的方法。但是,缓冲区打开的时间太长,总是500毫秒
var buffered = alternatingTrueFalse
.Buffer(TimeSpan.FromMilliseconds(500));
var output = buffered
.Subscribe(x => Console.WriteLine($"{TimeStamp} {string.Join(",", x)}"));
var isFlipped = buffered.Scan(false,
(x, y) =>
{
if (y.Count == 0)
{
return x;
}
return y.Count == 1 && y.First();
});
isFlipped.DumpTimes("Flipped");
我正试图找出如何使用窗口而不是缓冲区,以便在一个孤立的true之后,能够将开关翻转回第一个false。但我似乎不能正确地理解它,我还不太精通Rx,也不知道如何使用它的窗口打开/关闭值
示例输出
原创的
2017-10-07 20:21:39.302 True,False // Rapid values should not flip the switch (actually they should flip a different switch)
2017-10-07 20:21:39.797 True // Flip the switch here
2017-10-07 20:21:40.302 False // Flip the switch back and close the current window
2017-10-07 20:21:40.797 True // Flip the switch here
2017-10-07 20:21:41.297
2017-10-07 20:21:41.798 False // Etc...
...
2017-10-07 20:21:43.297 True
2017-10-07 20:21:43.800 False,True // Example of a window that is open too long, because it is not closed immediately upon the false value
...
缓冲+扫描
2017-10-07 20:47:15.154 True
2017-10-07 20:47:15.163 - Flipped-->True
2017-10-07 20:47:15.659 False,True // Example of a window open too long
2017-10-07 20:47:15.661 - Flipped-->False
这里有一个不使用
Scan
方法的解决方案
问题似乎是基于两个条件关闭缓冲区-最长时间或特定值。这个是基于
我建议不要基于其他操作符创建自定义操作符,因为这将占用更多的CPU和内存 下面是相同方法的干净版本
public static IObservable<IEnumerable<TValue>> BufferWithThrottle<TValue>(this IObservable<TValue> @this, int maxAmount, TimeSpan threshold)
{
var buffer = new List<TValue>();
return Observable.Create<IEnumerable<TValue>>(observer =>
{
var aTimer = new Timer();
void Clear()
{
aTimer.Stop();
buffer.Clear();
}
void OnNext()
{
observer.OnNext(buffer);
Clear();
}
aTimer.Interval = threshold.TotalMilliseconds;
aTimer.Enabled = true;
aTimer.Elapsed += (sender, args) => OnNext();
var subscription = @this.Subscribe(value =>
{
buffer.Add(value);
if (buffer.Count >= maxAmount)
OnNext();
else
{
aTimer.Stop();
aTimer.Start();
}
});
return Disposable.Create(() =>
{
Clear();
subscription.Dispose();
});
});
}
public static IObservable BufferWithThrottle(this,int maxAmount,TimeSpan阈值下的此IObservable)
{
var buffer=新列表();
返回可观察的。创建(观察者=>
{
var aTimer=新计时器();
无效清除()
{
停止();
buffer.Clear();
}
void OnNext()
{
observer.OnNext(缓冲区);
清除();
}
aTimer.Interval=threshold.total毫秒;
aTimer.Enabled=true;
aTimer.appead+=(发送方,参数)=>OnNext();
var subscription=@this.subscripte(值=>
{
缓冲区。添加(值);
如果(buffer.Count>=maxAmount)
OnNext();
其他的
{
停止();
aTimer.Start();
}
});
返回一次性。创建(()=>
{
清除();
subscription.Dispose();
});
});
}
谢谢,这似乎给了我一个很好的基础来继续。我仍然想了解更多有关窗口和扫描的信息,但groupbyuntill似乎也不错。仅供参考,我在这里的一个更有趣的场景中使用了这个方法:好的,但问题仍然是一样的-基于多个条件关闭缓冲区/窗口。我不确定你能用Scan
完成什么,因为你不能控制缓冲区/窗口的关闭。也许你可以直接把它应用到源观测上,然后从那里开始构建
alternatingTrueFalse.BufferWithClosingValue( TimeSpan.FromMilliseconds(500), false );
public static IObservable<IEnumerable<TValue>> BufferWithThrottle<TValue>(this IObservable<TValue> @this, int maxAmount, TimeSpan threshold)
{
var buffer = new List<TValue>();
return Observable.Create<IEnumerable<TValue>>(observer =>
{
var aTimer = new Timer();
void Clear()
{
aTimer.Stop();
buffer.Clear();
}
void OnNext()
{
observer.OnNext(buffer);
Clear();
}
aTimer.Interval = threshold.TotalMilliseconds;
aTimer.Enabled = true;
aTimer.Elapsed += (sender, args) => OnNext();
var subscription = @this.Subscribe(value =>
{
buffer.Add(value);
if (buffer.Count >= maxAmount)
OnNext();
else
{
aTimer.Stop();
aTimer.Start();
}
});
return Disposable.Create(() =>
{
Clear();
subscription.Dispose();
});
});
}