Col*_*nic 13 .net c# linq ienumerable lazy-evaluation
我写了一个SplitBetween类似的LINQ扩展方法String.Split.
> new List<int>(){3,4,2,21,3,2,17,16,1}
> .SplitBetween(x=>x>=10)
[3,4,2], [3,2], [], [1]
Run Code Online (Sandbox Code Playgroud)
资源:
// partition sequence into sequence of contiguous subsequences
// behaves like String.Split
public static IEnumerable<IEnumerable<T>> SplitBetween<T>(this IEnumerable<T> source,
Func<T, bool> separatorSelector,
bool includeSeparator = false)
{
var l = new List<T>();
foreach (var x in source)
{
if (separatorSelector(x))
{
if (includeSeparator)
{
l.Add(x);
}
yield return l;
l = new List<T>();
}
else
{
l.Add(x);
}
}
yield return l;
}
Run Code Online (Sandbox Code Playgroud)
在LINQ的精神下,我认为这种方法应该做懒惰的评估.但是,我的实现对外部IEnumerable进行了懒惰的评估,但没有超过内部的IEnumerable.我怎样才能解决这个问题?
演示外部行为是如何懒惰的.当任何人试图迭代它时,假设ThrowingEnumerable<int>是IEnumerable<int>爆炸(参见Skeet的Edulinq).
(new List<int>(){1,2,3,10,1})
.Concat(Extensions.ThrowingEnumerable<int>())
.SplitBetween(x=>x>=10)
.First().ToList();
[1,2,3]
Run Code Online (Sandbox Code Playgroud)
但内心的行为并不是懒惰的
(new List<int>(){1,2,3,10,1})
.Concat(Extensions.ThrowingEnumerable<int>())
.SplitBetween(x=>x>=10)
.ElementAt(2).First();
BOOM
Run Code Online (Sandbox Code Playgroud)
我们期待在这里1.
我想这是一个可以满足您要求的解决方案。
问题是您只有一种具有收益的方法,并且您在枚举外部集合时IEnumerable手动创建内部集合。第二个问题是,即使在下面的我的代码中,您的“测试”方式也会失败。然而,正如 David B 在他的评论中指出的那样,您必须遍历所有元素才能定义 external 的元素数量IEnumerable。但是您可以推迟内部 s 的创建和填充IEnumerable。
public static IEnumerable<IEnumerable<T>> SplitBetween<T>(this IEnumerable<T> source,Func<T,bool> separatorSelector, bool includeSeparators=false)
{
IList<T> sourceList = source.ToList();
var indexStart = 0;
var indexOfLastElement = sourceList.Count - 1;
for(int i = 0; i <= indexOfLastElement; i++)
if (separatorSelector(sourceList[i]))
{
if(includeSeparators)
yield return SplitBetweenInner(sourceList, indexStart, i);
else
yield return SplitBetweenInner(sourceList, indexStart, i - 1);
indexStart = i + 1;
}
else if(i == indexOfLastElement)
yield return SplitBetweenInner(sourceList, indexStart, i);
}
private static IEnumerable<T> SplitBetweenInner<T>(IList<T> source, int startIndex, int endIndex)
{
//throw new Exception("BOOM");
for(int i = startIndex; i <= endIndex; i++)
yield return source[i];
}
Run Code Online (Sandbox Code Playgroud)
请注意,它的行为与您的代码略有不同(当最后一个元素满足分隔符条件时,它不会创建另一个空列表 - 这取决于定义这里正确的内容,但我发现更好,因为行为与如果元素出现在源列表的开头)
如果您测试代码,您将看到内部IEnumerable执行被延迟。
如果抛出异常行未注释:
(new List<int>(){3,4,2,21,3,2,17,16,1}).SplitBetween(x=>x>=10, true).Count();
Run Code Online (Sandbox Code Playgroud)
回报4
(new List<int>(){3,4,2,21,3,2,17,16,1}).SplitBetween(x=>x>=10, true).First().Count();
Run Code Online (Sandbox Code Playgroud)
投掷BOOM
| 归档时间: |
|
| 查看次数: |
2820 次 |
| 最近记录: |