我写了跳过最后一个方法。当我使用 int 数组调用它时,我希望只返回 2 个元素,而不是 4 个。
怎么了?
谢谢
public static class myclass
{
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
{
return source.Reverse().Skip(n).Reverse();
}
}
class Program
{
static void Main(string[] args)
{
int [] a = new int[] {5, 6, 7, 8};
ArrayList a1 = new ArrayList();
a.SkipLast(2);
for( int i = 0; i <a.Length; i++)
{
Console.Write(a[i]);
}
}
}
调用为
var newlist = a.SkipLast(2);
for( int i = 0; i <newlist.Count; i++)
{
Console.Write(newlist[i]);
}
您的方法返回跳过的列表,但您的原始列表不会更新
如果要分配或更新相同的列表,可以将返回的列表设置回原始列表a = a.SkipLast(2).ToArray();
你应该分配结果,而不仅仅是a.SkipLast(2)
a = a.SkipLast(2).ToArray(); // <- if you want to change "a" and loop on a
for( int i = 0; i <a.Length; i++) { ...
当您执行a.SkipLast(2)
时,它会创建IEnumerable<int>
然后丢弃它;恕我直言,最易读的解决方案是使用 foreach,这对于 LINQ 非常方便:
...
int [] a = new int[] {5, 6, 7, 8};
foreach(int item in a.SkipLast(2))
Console.Write(item);
其他回复已经回答了您的问题,但更有效的实现不是这样吗(它不涉及制作数组的两个副本以将其反转两次)。不过,它确实迭代了集合两次(或者更确切地说,一次,然后 count-n 访问):
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
{
n = source.Count() - n;
return source.TakeWhile(_ => n-- > 0);
}
实际上,如果source
是一个无需迭代即可实现Count
的类型(例如数组或列表),则只会count-n
次访问元素,因此对于这些类型来说将非常有效。
这是一个更好的解决方案,它只迭代序列一次。它的数据要求使得它只需要一个包含n
元素的缓冲区,如果n
与序列的大小相比很小,这使得它非常有效:
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
{
int count = 0;
T[] buffer = new T[n];
var iter = source.GetEnumerator();
while (iter.MoveNext())
{
if (count >= n)
yield return buffer[count%n];
buffer[count++%n] = iter.Current;
}
}
将代码更改为,
foreach (var r in a.SkipLast(2))
{
Console.Write(r);
}
原因有三,
SkipLast
函数返回突变序列,它不会直接更改它。- 将索引器与
IEnumerable
一起使用有什么意义?它强加了一个不必要的计数。 - 此代码易于阅读、更易于键入并显示意图。
有关更有效的泛型SkipLast
请参阅带有枚举器的 Matthew 缓冲区。
您的示例可以使用更专业的SkipLast
,
public static IEnumerable<T> SkipLast<T>(this IList<T> source, int n = 1)
{
for (var i = 0; i < (source.Count - n); i++)
{
yield return source[i];
}
}