C# 有人能想出一个更好的枚举器版本吗?
我对下面的方法很满意。它接受一个可枚举的、已排序的、不相交的范围列表,并跳过不在范围内的项。如果范围为空,我们只需遍历每个项目。可枚举范围和范围列表都可能很大。我们希望此方法具有尽可能高的性能 有人能想出一个更优雅的代码吗?我主要对C#实现感兴趣,但如果有人有三个字符的APL实现,那也很酷C# 有人能想出一个更好的枚举器版本吗?,c#,ienumerable,enumerator,C#,Ienumerable,Enumerator,我对下面的方法很满意。它接受一个可枚举的、已排序的、不相交的范围列表,并跳过不在范围内的项。如果范围为空,我们只需遍历每个项目。可枚举范围和范围列表都可能很大。我们希望此方法具有尽可能高的性能 有人能想出一个更优雅的代码吗?我主要对C#实现感兴趣,但如果有人有三个字符的APL实现,那也很酷 public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source, List<Pair<int,
public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source, List<Pair<int, int>> ranges)
{
Debug.Assert(ranges == null || ranges.Count > 0);
int currentItem = 0;
Pair<int, int> currentRange = new Pair<int, int>();
int currentRangeIndex = -1;
bool betweenRanges = false;
if (ranges != null)
{
currentRange = ranges[0];
currentRangeIndex = 0;
betweenRanges = currentRange.First > 0;
}
foreach (T item in source)
{
if (ranges != null) {
if (betweenRanges) {
if (currentItem == currentRange.First)
betweenRanges = false;
else {
currentItem++;
continue;
}
}
}
yield return item;
if (ranges != null) {
if (currentItem == currentRange.Second) {
if (currentRangeIndex == ranges.Count - 1)
break; // We just visited the last item in the ranges
currentRangeIndex = currentRangeIndex + 1;
currentRange = ranges[currentRangeIndex];
betweenRanges = true;
}
}
currentItem++;
}
}
公共静态IEnumerable范围(IEnumerable源、列表范围)
{
Assert(ranges==null | | ranges.Count>0);
int currentItem=0;
对currentRange=新对();
int currentRangeIndex=-1;
bool-betweenRanges=false;
如果(范围!=null)
{
currentRange=范围[0];
currentRangeIndex=0;
betweenRanges=currentRange.First>0;
}
foreach(源中的T项)
{
如果(范围!=null){
如果(介于范围之间){
if(currentItem==currentRange.First)
betweenRanges=假;
否则{
currentItem++;
持续
}
}
}
收益回报项目;
如果(范围!=null){
if(currentItem==currentRange.Second){
if(currentRangeIndex==ranges.Count-1)
break;//我们刚刚访问了范围中的最后一项
currentRangeIndex=currentRangeIndex+1;
currentRange=范围[currentRangeIndex];
betweenRanges=真;
}
}
currentItem++;
}
}
可能在源代码上使用linq,例如:
public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source, List<Pair<int, int>> ranges)
{
if(ranges == null)
return null;
return source.Where((item, index) => ranges.Any(y => y.First < index && y.Second > index)).AsEnumerable();
}
公共静态IEnumerable范围(IEnumerable源、列表范围)
{
如果(范围==null)
返回null;
返回source.Where((item,index)=>ranges.Any(y=>y.Firstindex)).AsEnumerable();
}
我面前没有我的Windows PC,我不确定我是否正确理解了你的代码,但我尝试理解了你的文本,上面的代码可以工作。。。。或者类似的
更新:关于性能问题,我建议您使用一些简单的测试和计时功能来测试性能。可能会在源代码上使用linq,例如:
public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source, List<Pair<int, int>> ranges)
{
if(ranges == null)
return null;
return source.Where((item, index) => ranges.Any(y => y.First < index && y.Second > index)).AsEnumerable();
}
公共静态IEnumerable范围(IEnumerable源、列表范围)
{
如果(范围==null)
返回null;
返回source.Where((item,index)=>ranges.Any(y=>y.Firstindex)).AsEnumerable();
}
我面前没有我的Windows PC,我不确定我是否正确理解了你的代码,但我尝试理解了你的文本,上面的代码可以工作。。。。或者类似的
更新:关于性能问题,我建议您使用一些简单的测试和计时功能来测试性能。您可以将源列表复制到一个数组,然后针对每个范围,您可以阻止从新源数组复制到适当位置的目标数组。如果可以将源集合作为数组传入,那么这将是一种更好的方法。如果必须进行初始复制,则该操作的值为O(N)加上O(M),其中M是最终数组中的项目总数。因此,无论哪种情况,结果都是O(N)。您可以将源列表复制到一个数组,然后针对每个范围,您可以阻止从新源数组复制到正确位置的目标数组。如果可以将源集合作为数组传入,那么这将是一种更好的方法。如果必须进行初始复制,则该操作的值为O(N)加上O(M),其中M是最终数组中的项目总数。因此,无论哪种情况,结果都是O(N)。我的看法是这样的。我发现它更容易理解,如果不是更优雅的话
public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source, List<Tuple<int, int>> ranges)
{
if (ranges == null)
return source;
Debug.Assert(ranges.Count > 0);
return WalkRangesInternal(source, ranges);
}
static IEnumerable<T> WalkRangesInternal<T>(IEnumerable<T> source, List<Tuple<int, int>> ranges)
{
int currentItem = 0;
var rangeEnum = ranges.GetEnumerator();
bool moreData = rangeEnum.MoveNext();
using (var sourceEnum = source.GetEnumerator())
while (moreData)
{
// skip over every item in the gap between ranges
while (currentItem < rangeEnum.Current.Item1
&& (moreData = sourceEnum.MoveNext()))
currentItem++;
// yield all the elements in the range
while (currentItem <= rangeEnum.Current.Item2
&& (moreData = sourceEnum.MoveNext()))
{
yield return sourceEnum.Current;
currentItem++;
}
// advance to the next range
moreData = rangeEnum.MoveNext();
}
}
公共静态IEnumerable范围(IEnumerable源、列表范围)
{
如果(范围==null)
返回源;
断言(ranges.Count>0);
返回WalkRange内部(源、范围);
}
静态IEnumerable WalkRange内部(IEnumerable源、列表范围)
{
int currentItem=0;
var rangeEnum=ranges.GetEnumerator();
bool moreData=rangeEnum.MoveNext();
使用(var sourceEnum=source.GetEnumerator())
while(更多数据)
{
//跳过范围间隔中的每个项目
而(currentItem while(currentItem这是我的看法。我发现它更容易理解,如果不是更优雅的话
public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source, List<Tuple<int, int>> ranges)
{
if (ranges == null)
return source;
Debug.Assert(ranges.Count > 0);
return WalkRangesInternal(source, ranges);
}
static IEnumerable<T> WalkRangesInternal<T>(IEnumerable<T> source, List<Tuple<int, int>> ranges)
{
int currentItem = 0;
var rangeEnum = ranges.GetEnumerator();
bool moreData = rangeEnum.MoveNext();
using (var sourceEnum = source.GetEnumerator())
while (moreData)
{
// skip over every item in the gap between ranges
while (currentItem < rangeEnum.Current.Item1
&& (moreData = sourceEnum.MoveNext()))
currentItem++;
// yield all the elements in the range
while (currentItem <= rangeEnum.Current.Item2
&& (moreData = sourceEnum.MoveNext()))
{
yield return sourceEnum.Current;
currentItem++;
}
// advance to the next range
moreData = rangeEnum.MoveNext();
}
}
公共静态IEnumerable范围(IEnumerable源、列表范围)
{
如果(范围==null)
返回源;
断言(ranges.Count>0);
返回WalkRange内部(源、范围);
}
静态IEnumerable WalkRange内部(IEnumerable源、列表范围)
{
int currentItem=0;
var rangeEnum=ranges.GetEnumerator();
bool moreData=rangeEnum.MoveNext();
使用(var sourceEnum=source.GetEnumerator())
while(更多数据)
{
//跳过范围间隔中的每个项目
而(currentItem
public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source,
List<Pair<int, int>> ranges)
{
if (source == null)
throw new ArgumentNullException("source");
// If ranges is null, just return the source. From spec.
return ranges == null ? source : RangeIterate(source, ranges);
}
private static IEnumerable<T> RangeIterate<T>(IEnumerable<T> source,
List<Pair<int, int>> ranges)
{
// The key bit: a lazy sequence of all valid indices belonging to
// each range. No buffering.
var validIndices = from range in ranges
let start = Math.Max(0, range.First)
from validIndex in Enumerable.Range(start, range.Second - start + 1)
select validIndex;
int currentIndex = -1;
using (var indexErator = validIndices.GetEnumerator())
{
// Optimization: Get out early if there are no ranges.
if (!indexErator.MoveNext())
yield break;
foreach (var item in source)
{
if (++currentIndex == indexErator.Current)
{
// Valid index, yield.
yield return item;
// Move to the next valid index.
// Optimization: get out early if there aren't any more.
if (!indexErator.MoveNext())
yield break;
}
}
}
}
公共静态IEnumerable范围(IEnumerable源、,
列表范围)
{
我
public static IEnumerable<T> WalkRanges<T>(IEnumerable<T> source, List<Pair<int, int>> ranges)
{
int currentIndex = 0;
int currentRangeIndex = 0;
int maxRangeIndex = ranges.Length;
bool done = false;
foreach(var item in source)
{
if(currentIndex > range[currentRangeIndex].Second)
{
while(currentIndex > range[currentRangeIndex].Second)
{
if(!++currentRangeIndex < maxRangeIndex)
{
// We've passed last range =>
// set done = true to break outer loop and then break
done = true;
break;
}
}
if(currentIndex > range[currentRangeIndex].First)
yield item; // include if larger than first since we now it's smaller than second
}
else if(currentIndex > range[currentRangeIndex].First)
{
// If higher than first and lower than second we're in range
yield item;
}
if(done) // if done break outer loop
break;
currentIndex++; // always increase index when advancint through source
}
}