带有/LINQ 的复用器和解复用器
Posted
技术标签:
【中文标题】带有/LINQ 的复用器和解复用器【英文标题】:Mux & Demux w/ LINQ 【发布时间】:2011-01-24 11:10:28 【问题描述】:我正在尝试使用 LINQ to Objects 进行多路复用和多路分解,但在我看来这是一个非常棘手的问题。
查看此解复用器签名:
public static IEnumerable<IEnumerable<TSource>> Demux<TSource>(this IEnumerable<TSource> source, int multiplexity)
在抽象层面上这很容易,但理想情况下人们会想要
对源流保持懒惰 对每个多路复用流保持惰性 不要重复相同的元素你会怎么做?
我有点累,所以可能是我的注意力不集中在这里...
【问题讨论】:
【参考方案1】:假设您希望 (0, 1, 2, 3) 在解复用到两个流时以 (0, 2) 和 (1, 3) 结尾,那么如果没有缓冲,您基本上无法做到这一点。您可以仅在必要时缓冲,但这会很困难。基本上你需要能够应对两种相互矛盾的调用方式......
获取两个迭代器,并从每个迭代器中读取一项:
// Ignoring disposing of iterators etc
var query = source.Demux(2);
var demuxIterator = query.GetEnumerator();
demuxIterator.MoveNext();
var first = demuxIterator.Current;
demuxIterator.MoveNext();
var second = demuxIterator.Current;
first.MoveNext();
Console.WriteLine(first.Current); // Prints 0
second.MoveNext();
Console.WriteLine(second.Current); // Prints 1
或者获取一个迭代器,然后读取两个项目:
// Ignoring disposing of iterators etc
var query = source.Demux(2);
var demuxIterator = query.GetEnumerator();
demuxIterator.MoveNext();
var first = demuxIterator.Current;
first.MoveNext();
Console.WriteLine(first.Current); // Prints 0
first.MoveNext();
Console.WriteLine(first.Current); // Prints 2
在第二种情况下,它必须要么记住 1,要么能够重读它。
你有没有机会处理IList<T>
而不是IEnumerable<T>
?诚然,这将“破坏” LINQ to Objects 的其余部分 - 懒惰的投影等将成为过去。
请注意,这与GroupBy
之类的操作所遇到的问题非常相似——它们是延迟的,但不是懒惰的:一旦您开始从GroupBy
结果中读取,它就会读取整个输入数据.
【讨论】:
是的,我可以定义它,但是我喜欢它,因为它是一个爱好项目。但本着同样的精神,我希望有最佳的表现形式。但我认为 C# 语言中缺少允许这种情况发生的基本机制。我认为这是一个测试语言表达能力的非常有趣的案例。关于 GroupBy 的优点。如果我们能在这里真正的懒惰,那就太好了。还有例外 - 是的,这更加棘手,因为当您不知道必须处理多少嵌套流时,您不能使用“使用”。 我认为你对缓冲是正确的。也许创建一个具有一组固定成员的自定义 IList,每个成员都按照相同的可枚举和尊重顺序工作。很遗憾你不能直接表达它,但它可能已经足够好了。 @Bent:这并不是说语言中缺少任何东西 - 这是您尝试做的事情与源多路复用器的“仅转发”性质之间的根本不匹配。当您有两个迭代器(每个解复用序列一个)时,调用者可以从它们中的任何一个请求下一个项目 - 如果您没有随机访问并且不想缓冲,您将如何应对还是跳过数据? 我在想一个“平行”但依赖的枚举抽象。不过不用管它,可以用语言来表达。以上是关于带有/LINQ 的复用器和解复用器的主要内容,如果未能解决你的问题,请参考以下文章