有人能想出这个枚举器的更好版本吗?
我对以下方法非常满意。它需要一个可枚举的列表和一个排序的、不相交的范围列表,并跳过不在范围内的项目。如果范围为空,我们只遍历每个项目。可枚举项和范围列表都可能很大。我们希望这种方法具有尽可能高的性能。
有人能想出一段更优雅的代码吗?我主要对 C# 实现感兴趣,但如果有人有三字符 APL 实现,那也很酷。
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++;
}
}
I'm pretty happy with the following method. It takes an enumerable and a list of sorted, disjoint ranges and skips items not in the ranges. If the ranges are null, we just walk every item. The enumerable and the list of ranges are both possibly large. We want this method to be as high performance as possible.
Can someone think of a more elegant piece of code? I'm primarily interested in C# implementations, but if someone has a three-character APL implementation, that's cool too.
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++;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
也许在你的
source
上使用linq,比如:我面前没有Windows PC,我不确定我是否正确理解了你的代码,但我试图理解你的文本,并且上面的代码可以工作......或类似的东西。
更新:关于性能问题,我建议您通过一些简单的测试和时间这两个函数来测试性能。
Maybe use linq on your
source
something like:I don't have my Windows PC in front of me and I'm not sure I understood your code correctly, but I tried to understand your text instead and the code above could work.... or something like it.
UPDATED: Regarding the performance issue I would recommend you to test the performance with some simple test and time both of the functions.
您可以将源列表复制到数组,然后对于每个范围,您可以阻止从新源数组到适当位置的目标数组的复制。如果您可以将源集合作为数组传入,那么这将是一种更好的方法。如果您确实必须执行初始复制,则该操作的复杂度为 O(N),再加上 O(M),其中 M 是最终数组中的项目总数。所以无论哪种情况,最终结果都是 O(N)。
You could copy the source list to an array and then for each range, you can block copy from your new source array to a target array in the proper location. If you can get your source collection passed in as an array, that would make this an even better approach. If you do have to do the initial copy, it is O(N) for that operation plus O(M) where M is the total number of items in the final array. So it ends up coming out to O(N) in either case.
这是我的看法。我发现它更容易理解,甚至更优雅。
Here's my take. I find it easier to understand, if not more elegant.
这个怎么样(未经测试)?应该具有非常相似的性能特征(纯流,没有不必要的缓冲,快速退出),但更容易遵循,IMO:
如果你不介意缓冲索引,你可以做这样的事情,这更清楚,IMO:
How about this (untested)? Should have pretty similar performance characteristics (pure streaming, no unnecessary buffering, quick exit), but is easier to follow, IMO:
If you don't mind buffering indices, you can do something like this, which is even more clearer, IMO:
您可以手动迭代集合,以防止枚举器在跳过当前项目时获取当前项目:
You could iterate over the collection manually to prevent the enumerator from getting the current item when it will be skipped:
我的第二次尝试,这将考虑范围的排序。我还没有尝试过,但我认为它有效:)。您可能可以将一些代码提取到更小的函数中,以使其更具可读性。
My second try, this will consider the ordering of the ranges. I haven' tried it yet but I thinkt it works :). You could probably extract some of the code to smaller functions to make it more readable.