C#中如何使用切片操作旋转列表
在Python中,我可以把一个列表my_list的内容旋转一下:
>>> my_list = list(range(10))
>>> my_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> new_list = my_list[1:] + my_list[:1]
>>> new_list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
那么在C#中,有什么方法可以创建一个新的列表,这个新列表是由一个已有的C#列表的两个部分组合而成的呢?我知道如果需要的话可以用比较笨的方法来实现。
6 个回答
在C#中,最接近的做法就是使用 Enumerable.Skip 和 Enumerable.Take 这两个扩展方法。你可以用它们来创建你新的列表。
你可以很简单地用LINQ来做到这一点:
// Create the list
int[] my_list = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
IEnumerable<int> new_list =
my_list.Skip(1).Concat(my_list.Take(1));
你甚至可以把这个作为一个扩展方法来添加,像这样:
public static IEnumerable<T> Slice<T>(this IEnumerable<T> e, int count)
{
// Skip the first number of elements, and then take that same number of
// elements from the beginning.
return e.Skip(count).Concat(e.Take(count));
}
当然,上面的代码需要一些错误检查,但大体思路就是这样。
再想想,这个算法还有很多可以改进的地方,这样可以提高性能。
如果你的 IEnumerable<T>
实例实现了 IList<T>
或者是一个数组,那就可以充分利用它是有索引的这个特点。
另外,你还可以减少在消息主体中需要跳过和获取的迭代次数。
举个例子,如果你有200个项目,而你想要切片到199,那么在Slice方法的主体中需要进行199次(初始跳过) + 1次(剩下的项目) + 199次(获取)的迭代。这可以通过只遍历一次列表来减少,遍历时把项目存储在一个列表中,然后将这个列表与自己连接(这样就不需要再迭代了)。
在这种情况下,权衡的就是内存使用。
为此,我建议扩展方法可以这样写:
public static IEnumerable<T> Slice<T>(this IEnumerable<T> source, int count)
{
// If the enumeration is null, throw an exception.
if (source == null) throw new ArgumentNullException("source");
// Validate count.
if (count < 0) throw new ArgumentOutOfRangeException("count",
"The count property must be a non-negative number.");
// Short circuit, if the count is 0, just return the enumeration.
if (count == 0) return source;
// Is this an array? If so, then take advantage of the fact it
// is index based.
if (source.GetType().IsArray)
{
// Return the array slice.
return SliceArray((T[]) source, count);
}
// Check to see if it is a list.
if (source is IList<T>)
{
// Return the list slice.
return SliceList ((IList<T>) source);
}
// Slice everything else.
return SliceEverything(source, count);
}
private static IEnumerable<T> SliceArray<T>(T[] arr, int count)
{
// Error checking has been done, but use diagnostics or code
// contract checking here.
Debug.Assert(arr != null);
Debug.Assert(count > 0);
// Return from the count to the end of the array.
for (int index = count; index < arr.Length; index++)
{
// Return the items at the end.
yield return arr[index];
}
// Get the items at the beginning.
for (int index = 0; index < count; index++)
{
// Return the items from the beginning.
yield return arr[index];
}
}
private static IEnumerable<T> SliceList<T>(IList<T> list, int count)
{
// Error checking has been done, but use diagnostics or code
// contract checking here.
Debug.Assert(list != null);
Debug.Assert(count > 0);
// Return from the count to the end of the list.
for (int index = count; index < list.Count; index++)
{
// Return the items at the end.
yield return list[index];
}
// Get the items at the beginning.
for (int index = 0; index < list.Count; index++)
{
// Return the items from the beginning.
yield return list[index];
}
}
// Helps with storing the sliced items.
internal class SliceHelper<T> : IEnumerable<T>
{
// Creates a
internal SliceHelper(IEnumerable<T> source, int count)
{
// Test assertions.
Debug.Assert(source != null);
Debug.Assert(count > 0);
// Set up the backing store for the list of items
// that are skipped.
skippedItems = new List<T>(count);
// Set the count and the source.
this.count = count;
this.source = source;
}
// The source.
IEnumerable<T> source;
// The count of items to slice.
private int count;
// The list of items that were skipped.
private IList<T> skippedItems;
// Expose the accessor for the skipped items.
public IEnumerable<T> SkippedItems { get { return skippedItems; } }
// Needed to implement IEnumerable<T>.
// This is not supported.
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator()
{
throw new InvalidOperationException(
"This operation is not supported.");
}
// Skips the items, but stores what is skipped in a list
// which has capacity already set.
public IEnumerator<T> GetEnumerator()
{
// The number of skipped items. Set to the count.
int skipped = count;
// Cycle through the items.
foreach (T item in source)
{
// If there are items left, store.
if (skipped > 0)
{
// Store the item.
skippedItems.Add(item);
// Subtract one.
skipped--;
}
else
{
// Yield the item.
yield return item;
}
}
}
}
private static IEnumerable<T> SliceEverything<T>(
this IEnumerable<T> source, int count)
{
// Test assertions.
Debug.Assert(source != null);
Debug.Assert(count > 0);
// Create the helper.
SliceHelper<T> helper = new SliceHelper<T>(
source, count);
// Return the helper concatenated with the skipped
// items.
return helper.Concat(helper.SkippedItems);
}
在编程中,有时候我们会遇到一些问题,可能是代码运行不正常,或者是我们不明白某个功能是怎么工作的。这种情况下,很多人会选择去StackOverflow这个网站寻求帮助。这个网站就像一个大型的问答社区,程序员们可以在这里提问,也可以回答别人的问题。
在提问时,最好把问题描述得清楚一些,比如你遇到的具体情况、你使用的代码,以及你希望得到什么样的帮助。这样,其他人才能更好地理解你的问题,并给出有效的建议。
同时,查看别人提问和回答的问题也是一个很好的学习方式。通过阅读这些内容,你可以了解到很多编程的技巧和解决方案,这对提高自己的编程能力非常有帮助。
总之,StackOverflow是一个很有用的资源,适合所有想要学习编程的人。无论你是新手还是有经验的程序员,都能在这里找到有价值的信息。
var newlist = oldlist.Skip(1).Concat(oldlist.Take(1));