【发布时间】:2012-11-20 12:25:46
【问题描述】:
我有两个数据源。
其中一个是缓存列表,另一个是通过IObservable<T>推送的新数据。
我想使用 Rx 找出需要对缓存列表 A 执行哪些操作,以使其顺序和内容与新数据相同。
我正在寻找一个函数,它接受 IEnumerable<T> a 和 IObservable<T> b 并返回一个可观察对象,在 a 上推送操作(插入和删除),这将使其与 b 相同,而无需等待b 完成。
注意:我知道我不能修改列表或 observable。我不想。
我只想知道什么操作,以什么顺序,只要这些操作变得已知。
a 和 b 都是唯一且已排序的,T 实现了 IComparable<T> 和 IEquatable<T>。
public static IObservable<Tuple<int, bool>> IndexDelta<T>(
IEnumerable<T> a,
IObservable<T> b
) where T : IEquatable<T>, IComparable<T> {
// ???
}
我将在我的示例中使用ints。
什么?!
考虑这两个序列:
A: [150, 100, 70, 30, 20]
B: [300, 200, 100, 70, 60, 50, 20]
目标是找到一系列将A转换为B的删除/插入操作。认为A是缓存数据源,B是新数据,我想知道如何将这些更新转换为网格无需重新加载。
行在两个来源中排序。
我希望输出在表单中
[(0, true), (1, true), (0, false), (3, false), (4, true), (5, true)]
我稍后会按布尔标志对这些操作进行分组:
deleted: [0, 3]
inserted: [0, 1, 4, 5]
这将翻译成人类语言
删除A0和A3:
A = [
150, 100, 70,30, 20] = [100, 70, 20]将B0、B1、B4、B5插入A:
A = [300, 200, 100, 70, 60, 50, 20]
现在 A 与 B 相同。
要求
我要注意几件重要的事情:
A 是保证不会更改的列表。 B 是一个冷的 observable,它需要一些时间才能完成,但很快就会产生第一个项目。因此,结果 observable需要在有足够数据可用时立即推送。
项目在两个来源中保证是唯一的
IEquatable<T>。项目是不可变的,保证在两个来源中都使用
IComparable<T>降序排序。最好优化添加到 B 左侧的新项目。这是最常见的情况。然而,考虑到它们的时间戳是适当的(不会破坏排序),项目可能会被删除或插入到任何其他位置。想想 iPhone 相机胶卷。
(*) 如果可能的话,我对纯函数解决方案感兴趣。
伪代码草图
我草拟了一个伪代码算法,它以命令式的方式实现这一点。
我编造了Current、MoveNext、await 和yield push 语义,但这个想法应该有点道理。
IObservable<Tuple<int, bool>> IndexDelta(a, b)
{
var indexA = 0;
var indexB = 0;
while (true) {
var headA = a.Current;
var headB = b.Current;
if (headA == null && headB == null) {
return yield break; // both sequences are over
}
var reportDeletion = () => {
yield push Tuple.Create(indexA, false);
await a.MoveNext(); // this one is fast
}
var reportInsertion = () => {
yield push Tuple.Create(indexB, true);
await b.MoveNext(); // can take a long time
}
if (headA == null) { // No source item at this position
reportInsertion();
continue;
}
if (headB == null) { // No fetched item at this position
reportDeletion();
continue;
}
switch (headB.CompareTo(headA)) {
case 0:
yield continue;
break;
case 1: // Fetched item is newer than source item
reportInsertion();
break;
case -1: // Source item is newer than fetched item
reportDeletion();
break;
}
indexA++;
indexB++;
}
}
我相信你可以用Subject<T> 实现一些非常相似的东西。但是我不想继续这个解决方案,因为我想知道是否可以纯粹通过组合 Rx 函数来解决它,例如 Aggregate、Zip 或 CombineLatest。
你有什么想法?
【问题讨论】:
-
这不太合理。您谈论 IEnumerable 或 IObservable 上的插入和删除,但这些不是列表。您只能从中获得下一个价值。您不能从中插入或删除。您的伪代码草图也完全错误,因为 IObservable 不能从具有 yield 的函数创建。只有 IEnumerables 可以使用 yield 神奇地生成。你了解 IEnumerable 和 IOBservable 之间的区别以及它们的用途吗?
-
@brad:我的伪代码是 pseudo 是有原因的。是的,我知道我不能这样做,我只是概述了可以通过推送到
Subject来实现的算法,如果可以使用纯 Rx,我不想使用它。 -
但是你还是讲到IEnumerable的插入和删除。正如我所说,它不是一个列表。插入或删除到 IEnumerable 是什么意思。你是说列表吗?
-
我可能还不够清楚:我不想转换我的输入。我不想更改
a或b。我想获得a和b在插入和删除方面的差异列表。 -
你想要两个列表上的差异算法吗?
标签: c# linq system.reactive set-difference