我有一种感觉,这可能是一个非常简单的扩展方法,我已经错过但我看不到它...
我基本上想要一个生成流的流,其中值在每个新值上缓慢递增。我希望节流/采样,而不是时间,而是“容忍”。 e.g。
var ob = Enumerable.Range(0, 30).ToObservable(); // 0, 1, 2, 3, 4, 5,....., 30
var largeMovingOb = ob.WhenChangedBy(10); // 0, 10, 20, 30
当我有[1,4,20,33]等序列时,我希望在值改变超过最后一个的15时输出 - 这将导致:[1,20]。如果值变化为12将导致:[1,20,33]
这是否有内置的Rx扩展?理想情况下,它可以在所有数字类型上工作,而无需为每个数字类型编写重载。
答案 0 :(得分:7)
我认为这非常适合Observable.Scan
var ob = Enumerable.Range(0, 30).ToObservable();
var largeMovingOb = ob.Scan((acc, i) => acc + 10 > i ? acc : i)
.DistinctUntilChanged();
答案 1 :(得分:0)
您可以重新调整用途的另一个内置运算符是DistinctUntilChanged,它将跟踪您的最后一个值。这里最大的“黑客”是IEqualityComparer
可能不遵循标准的平等期望(a == b && b == c
并不意味着a == c
取决于函数)
public static IObservable<T> DistinctUntilChangedBy<T>(
this IObservable<T> source, Func<T, T, bool> isChanged)
{
//check arguments
return source.DistinctUntilChanged(new MarginEqualityComparer<T>(isChanged));
}
class MarginEqualityComparer<T> : IEqualityComparer<T>
{
MarginEqualityComparer(Func<T, T, bool> comparer)
{
_comparer = comparer;
}
private readonly Func<T, T, bool> _comparer;
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
throw new NotSupportedException("This comparer does not support hashing.");
}
}
答案 2 :(得分:0)
有一个内置的操作符可以完成你想要的操作。
试试这个:
var ob = Observable.Range(0, 30);
var largeMovingOb = ob.DistinctUntilChanged(x => x / 10);
它可以用于任何类型,而不仅仅是数字类型,因为签名如下所示:
IObservable<TSource> DistinctUntilChanged<TSource, TKey>(
this IObservable<TSource> source, Func<TSource, TKey> keySelector)
简单。
答案 3 :(得分:-1)
您可以使用Where
extension method上的IObservable<T>
,跟踪上次产生的内容,并且只有当值超出最后一个产生的公差值时,谓词才会返回true。
这可以包含在一个扩展方法中,利用闭包来执行此操作,如下所示:
public static IObservable<int> WhenLastObservedChangesByMoreThan(
this IObservable<int> observable, int tolerance)
{
// Validate parameters.
if (observable == null) throw new ArgumentNullException("observable");
// Tolerance must be positive, so comparisons are correct after
// addition/subtraction.
if (tolerance < 0)
throw new ArgumentOutOfRangeExeption("tolerance", tolerance,
"The tolerance parameter must be a non-negative number.");
// Shortcut: If tolerance is 0, then every value is returned, just
// return the observable.
if (tolerance == 0) return observable;
// The last value yielded.
int? lastYielded = null;
// Filter.
observable = observable.Where(i => {
// If there is a previous value
// that was yielded.
if (lastYielded != null)
{
// Is the last value within
// tolerance?
if (i - tolerance < i && i < i + tolerance)
{
// Do not process.
return false;
}
}
// This is being yielded, store the value.
lastYielded = i;
// Yield the value.
return true;
});
}
请注意,上述内容不是线程安全的,如果IObservable<T>
从多个线程调用OnNext
,那么您必须锁定对lastYielded
变量的访问权限(这很容易足以使用lock
statement)。