我创造了一个游戏,其中有一个可观察的事件流X,代表制造商交付的产品。还有一些外部事件(让他们称之为变形金刚)以各种方式和不同的时间段影响制造业的表现。我希望通过其他可观察对象来表示,它发出一个转换X的函数,应该应用于每个X直到Transformer的OnComplete。变形金刚的数量不为人所知 - 它们是由用户操作(如设备购买)或随机生成(如设备故障)而创建的。
我想我需要IObservable<IObservable<Func<X,X>>>
我需要Join
(Zip
,还有其他什么?)IObservable<X>
才能执行此操作。你能帮帮我吗? Observable.CombineLatest
几乎是我需要的,但它需要IEnumerable<IObservable<T>>
。
如果我的描述不清楚,这里是一张大理石图:
用更抽象的术语来说,我需要的东西非常类似于矩阵的转置,而不是List<List<T>>
我有IObservable<IObservable<T>>
。
答案 0 :(得分:3)
假设你的变形金刚在int
上工作,你的观察者的名字是这样的:
IObservable<IObservable<Func<int, int>>> transformerObservables = null;
IObservable<int> values = null;
我首先将Observable of Observable of transformer转换为Observable of Transform of Transformers,即
IObservable<IObservable<Func<int, int>>> -> IObservable<<Func<int, int>>[]>
首先,我们最终希望在列表中添加和删除函数,并确保删除正确的转换器,我们必须覆盖Func&lt; ...&gt;上的常用比较机制。所以我们......
var transformerArrayObservable = transformerObservables
// ...attach each transformer the index of the observable it came from:
.Select((transformerObservable, index) => transformerObservable
.Select(transformer => Tuple.Create(index, transformer))
// Then, materialize the transformer sequence so we get noticed when the sequence terminates.
.Materialize()
// Now the fun part: Make a scan, resulting in an observable of tuples
// that have the previous and current transformer
.Scan(new
{
Previous = (Tuple<int, Func<int, int>>)null,
Current = (Tuple<int, Func<int, int>>)null
},
(tuple, currentTransformer) => new
{
Previous = tuple.Current,
Current = currentTransformer.HasValue
? currentTransformer.Value
: (Tuple<int, Func<int, int>>)null
}))
// Merge these and do another scan, this time adding and removing
// the transformers from a list.
.Merge()
.Scan(
new Tuple<int, Func<int, int>>[0],
(array, tuple) =>
{
//Expensive! Consider taking a dependency on immutable collections here!
var list = array.ToList();
if (tuple.Previous != null)
list.Remove(tuple.Previous);
if (tuple.Current != null)
list.Add(tuple.Current);
return list.ToArray();
})
// Extract only the actual functions
.Select(x => x.Select(y => y.Item2).ToArray())
// Finally, to make sure that values are passed even when no transformer has been observed
// start this sequence with the neutral transformation.
// IMPORTANT: You should test what happens when the first value is oberserved very quickly. There might be timing issues.
.StartWith(Scheduler.Immediate, new[] { new Func<int, int>[0]});
现在,您将需要一个在Rx中不可用的运算符,称为CombineVeryLatest。看看here。
var transformedValues = values
.CombineVeryLatest(transformerArrayObservable, (value, transformers) =>
{
return transformers
.Aggregate(value, (current, transformer) => transformer(current));
});
你应该完成。我确信可以获得一些表现,但你会得到这个想法。
答案 1 :(得分:2)
灵感来自this answer我最终得到了这个:
Output = Input
.WithLatestFrom(
transformations.Transpose(),
(e, fs) => fs.Aggregate(e, (x, f) => f(x)))
.SelectMany(x => x)
.Publish();
其中Transpose和WithLatestFrom运算符定义为:
public static IObservable<IObservable<T>> Transpose<T>(this IObservable<IObservable<T>> source)
{
return Observable.Create<IObservable<T>>(o =>
{
var latestValues = new Dictionary<IObservable<T>, T>();
var result = new BehaviorSubject<IObservable<T>>(Observable.Empty<T>());
source.Subscribe(observable =>
{
observable.Subscribe(t =>
{
latestValues[observable] = t;
result.OnNext(latestValues.ToObservable().Select(kv => kv.Value));
}, () =>
{
latestValues.Remove(observable);
});
});
return result.Subscribe(o);
});
}
public static IObservable<R> WithLatestFrom<T, U, R>(
this IObservable<T> source,
IObservable<U> other,
Func<T, U, R> combine)
{
return Observable.Create<R>(o =>
{
var current = new BehaviorSubject<U>(default(U));
other.Subscribe(current);
return source.Select(s => combine(s, current.Value)).Subscribe(o);
});
}
这是检查行为的单元测试:
[TestMethod]
public void WithLatestFrom_ShouldNotDuplicateEvents()
{
var events = new Subject<int>();
var add1 = new Subject<Func<int, int>>();
var add2 = new Subject<Func<int, int>>();
var transforms = new Subject<IObservable<Func<int, int>>>();
var results = new List<int>();
events.WithLatestFrom(
transforms.Transpose(),
(e, fs) => fs.Aggregate(e, (x, f) => f(x)))
.SelectMany(x => x)
.Subscribe(results.Add);
events.OnNext(1);
transforms.OnNext(add1);
add1.OnNext(x => x + 1);
events.OnNext(1); // 1+1 = 2
transforms.OnNext(add2);
add2.OnNext(x => x + 2);
events.OnNext(1); // 1+1+2 = 4
add1.OnCompleted();
events.OnNext(1); // 1+2 = 3
add2.OnCompleted();
events.OnNext(1);
CollectionAssert.AreEqual(new int[] { 1, 2, 4, 3, 1 }, results);
}
答案 2 :(得分:2)
哇,这是一个真正的心灵弯曲,但我认为我有一些有用的东西。首先,我创建了一种将IObservable<IObservable<Func<T, T>>
转换为IObservable<IEnumerable<Func<T, T>>
的扩展方法。扩展方法在假设每个observable在完成之前只产生一个Func<T, T>
的情况下运行。
public static class MoreReactiveExtensions
{
public static IObservable<IEnumerable<Func<T, T>>> ToTransformations<T>(this IObservable<IObservable<Func<T, T>>> source)
{
return
Observable
// Yield an empty enumerable first.
.Repeat(Enumerable.Empty<Func<T, T>>(), 1)
// Then yield an updated enumerable every time one of
// the transformation observables yields a value or completes.
.Concat(
source
.SelectMany((x, i) =>
x
.Materialize()
.Select(y => new
{
Id = i,
Notification = y
}))
.Scan(
new List<Tuple<int, Func<T, T>>>(),
(acc, x) =>
{
switch(x.Notification.Kind)
{
// If an observable compeleted then remove
// its corresponding function from the accumulator.
case NotificationKind.OnCompleted:
acc =
acc
.Where(y => y.Item1 != x.Id)
.ToList();
break;
// If an observable yield a new Func then add
// it to the accumulator.
case NotificationKind.OnNext:
acc = new List<Tuple<int, Func<T, T>>>(acc)
{
Tuple.Create(x.Id, x.Notification.Value)
};
break;
// Do something with exceptions here.
default:
// Do something here
break;
}
return acc;
})
// Select an IEnumerable<Func<T, T>> here.
.Select(x => x.Select(y => y.Item2)));
}
}
然后,给出以下变量:
IObservable<IObservable<Func<int, int>>> transformationObservables
IObservable<int> products`
我像这样使用它:
var transformations =
transformationObservables
.ToTransformations()
.Publish()
.RefCount();
IObservable<int> transformedProducts=
transformations
.Join(
products,
t => transformations,
i => Observable.Empty<int>(),
(t, i) => t.Aggregate(i, (ii, tt) => tt.Invoke(ii)))
根据我的测试,结果似乎是正确的。
答案 3 :(得分:0)
将变形金刚表示为流是否有意义?
看到添加一个新的Transformer只能转换未来的事件,为什么不维护一些活跃的变换器,然后当一个新的事件进来时,你可以应用所有当前的变形金刚?
当Transformer不再处于活动状态时,它将从集合中删除或标记为非活动状态。