我写了跳过最后一个方法。当我用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]);
}
}
}
答案 0 :(得分:5)
你需要打电话给
var newlist = a.SkipLast(2);
for( int i = 0; i <newlist.Count; i++)
{
Console.Write(newlist[i]);
}
您的方法返回跳过的列表,但原始列表不会更新
如果要分配或更新相同的列表,可以将返回的列表设置为原始的a = a.SkipLast(2).ToArray();
答案 1 :(得分:3)
你应该分配结果,而不仅仅是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);
答案 2 :(得分:1)
将您的代码更改为
foreach (var r in a.SkipLast(2))
{
Console.Write(r);
}
有三个原因,
SkipLast
函数返回变异序列,它不会直接更改它。 IEnumerable
的索引器有什么意义?它强加了不必要的数量。要获得更有效的通用SkipLast
,请参阅Matthew's buffer with enumerator.
您的示例可以使用更专业的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];
}
}
答案 3 :(得分:1)
其他回复已经回答了你的问题,但不会更有效的实现(这不涉及制作数组的两个副本以便将其反转两次)。它确实迭代了集合两次(或者更确切地说,一次,然后是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
的类型(例如数组或List),那么这只会访问元素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;
}
}