C#相当于使用python slice操作旋转列表

在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#中的等价方法是创build一个由两个现有C#列表组成的新列表? 我知道如果有必要,我可以通过蛮力产生。

 var newlist = oldlist.Skip(1).Concat(oldlist.Take(1)); 

你可以很容易地使用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)); } 

上面当然需要进行一些错误检查,但这是一般的前提。


更多地思考这个问题,可以对这个algorithm进行一些改进,从而提高性能。

如果IEnumerable<T>实例实现了IList<T>或者是一个数组,并利用索引的事实,那么您肯定可以利用它。

此外,您可以减less跳过和消耗所需的迭代次数。

例如,如果您有200个项目,并且您想要使用199的值进行切片,则需要199(对于初始跳过)+ 1(对于剩余项目)+ 199(对于获取)迭代切片法。 这可以通过遍历列表来减less,将项目存储在列表中,然后连接到自己(不需要迭代)。

在这种情况下,这里的交易是记忆。

为此,我提出如下扩展方法:

 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); } 

C#中最接近的就是使用Enumerable.Skip和Enumerable.Take扩展方法。 你可以使用这些来build立你的新列表。

 List<int> list1; List<int> list2 = new List<int>(list1); 

或者你可以

 list2.AddRange(list1); 

使用LINQ获得一个清晰的列表

 List<int> distinceList = list2.Distinct<int>().ToList<int>(); 

要旋转数组,请执行a.Slice(1, null).Concat(a.Slice(null, 1))

这是我的刺伤。 a.Slice(step: -1)作为a[::-1]给出反转的拷贝。

 /// <summary> /// Slice an array as Python. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="array"></param> /// <param name="start">start index.</param> /// <param name="end">end index.</param> /// <param name="step">step</param> /// <returns></returns> /// <remarks> /// http://docs.python.org/2/tutorial/introduction.html#strings /// +---+---+---+---+---+ /// | H | e | l | p | A | /// +---+---+---+---+---+ /// 0 1 2 3 4 5 /// -6 -5 -4 -3 -2 -1 /// </remarks> public static IEnumerable<T> Slice<T>(this T[] array, int? start = null, int? end = null, int step = 1) { array.NullArgumentCheck("array"); // step if (step == 0) { // handle gracefully yield break; } // step > 0 int _start = 0; int _end = array.Length; // step < 0 if (step < 0) { _start = -1; _end = -array.Length - 1; } // inputs _start = start ?? _start; _end = end ?? _end; // get positive index for given index Func<int, int, int> toPositiveIndex = (int index, int length) => { return index >= 0 ? index : index + length; }; // start if (_start < -array.Length || _start >= array.Length) { yield break; } _start = toPositiveIndex(_start, array.Length); // end if (_end < -array.Length - 1) { yield break; } if (_end > array.Length) { _end = array.Length; } _end = toPositiveIndex(_end, array.Length); // slice if (step > 0) { // start, end if (_start > _end) { yield break; } for (int i = _start; i < _end; i += step) { yield return array[i]; } } else { // start, end if (_end > _start) { yield break; } for (int i = _start; i > _end; i += step) { yield return array[i]; } } } 

nunittesting:

 [Test] // normal cases [TestCase(3, 5, 1, 3, 4)] [TestCase(0, 5, 1, 0, 4)] [TestCase(3, null, 1, 3, 9)] [TestCase(0, null, 1, 0, 9)] [TestCase(null, null, 1, 0, 9)] [TestCase(0, 10, 1, 0, 9)] [TestCase(0, int.MaxValue, 1, 0, 9)] [TestCase(-1, null, 1, 9, 9)] [TestCase(-2, null, 1, 8, 9)] [TestCase(0, -2, 1, 0, 7)] // corner cases [TestCase(0, 0, 1, null, null)] [TestCase(3, 5, 2, 3, 3)] [TestCase(3, 6, 2, 3, 5)] [TestCase(100, int.MaxValue, 1, null, null)] [TestCase(int.MaxValue, 1, 1, null, null)] [TestCase(-11, int.MaxValue, 1, null, null)] [TestCase(-6, -5, 1, 4, 4)] [TestCase(-5, -6, 1, null, null)] [TestCase(-5, -5, 1, null, null)] [TestCase(0, -10, 1, null, null)] [TestCase(0, -11, 1, null, null)] [TestCase(null, null, 100, 0, 0)] // -ve step [TestCase(null, null, -1, 9, 0)] [TestCase(-7, -5, -1, null, null)] [TestCase(-5, -7, -1, 5, 4)] [TestCase(-5, -7, -2, 5, 5)] [TestCase(-7, null, -1, 3, 0)] public void Slice01(int? s, int? e, int i, int? first, int? last) { var a = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var slice = a.Slice(start: s, end: e, step: i).ToArray(); Print(slice); if (first.HasValue) { Assert.AreEqual(first, slice.First()); } if (last.HasValue) { Assert.AreEqual(last, slice.Last()); } }