我有以下扩展方法来查找序列中的元素,然后返回两个IEnumerable< T> s:一个包含该元素之前的所有元素,一个包含该元素和后面的所有元素.我更喜欢这种方法是懒惰的,但我还没有找到办法做到这一点.任何人都可以提出解决方案吗?
public static PartitionTuple<T> Partition<T>(this IEnumerable<T> sequence,Func<T,bool> partition) { var a = sequence.ToArray(); return new PartitionTuple<T> { Before = a.TakeWhile(v => !partition(v)),After = a.SkipWhile(v => !partition(v)) }; }
执行sequence.ToArray()会立即失去懒惰的要求.然而,没有该行,可以迭代昂贵的迭代序列两次.而且,取决于调用代码的作用,还有很多次.
解决方法
这是一个有趣的问题,要做到正确,你必须知道“正确”是什么.对于操作的语义,我认为这个定义是有道理的:
>即使生成的序列被多次枚举,源序列也只被枚举一次.
>在枚举其中一个结果之前,不会枚举源序列.
>每个结果都应该可以独立枚举.
>如果源序列发生变化,则未定义将发生什么.
我不完全确定我正确处理了匹配对象,但我希望你能得到这个想法.我将很多工作推迟到PartitionTuple< T>上课可以偷懒.
public class PartitionTuple<T> { IEnumerable<T> source; IList<T> before,after; Func<T,bool> partition; public PartitionTuple(IEnumerable<T> source,bool> partition) { this.source = source; this.partition = partition; } private void EnsureMaterialized() { if(before == null) { before = new List<T>(); after = new List<T>(); using(var enumerator = source.GetEnumerator()) { while(enumerator.MoveNext() && !partition(enumerator.Current)) { before.Add(enumerator.Current); } while(!partition(enumerator.Current) && enumerator.MoveNext()); while(enumerator.MoveNext()) { after.Add(enumerator.Current); } } } } public IEnumerable<T> Before { get { EnsureMaterialized(); return before; } } public IEnumerable<T> After { get { EnsureMaterialized(); return after; } } } public static class Extensions { public static PartitionTuple<T> Partition<T>(this IEnumerable<T> sequence,bool> partition) { return new PartitionTuple<T>(sequence,partition); } }