[英]C# equivalent of rotating a list using python slice operation
在 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# 列表的兩個切片組成的新列表的等效方法是什么? 我知道如有必要,我可以通過蠻力生成。
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));
}
當然上面需要一些錯誤檢查,但這是一般前提。
進一步考慮這一點,可以對該算法進行明確的改進,從而提高性能。
如果IEnumerable<T>
實例實現了IList<T>
或者是一個數組,你絕對可以利用它,利用它被索引的事實。
此外,您可以減少在消息正文中跳過和執行所需的迭代次數。
例如,如果您有 200 個項目並且您想以 199 的值進行切片,那么它需要 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);
}
在 C# 中最接近的是使用Enumerable.Skip和Enumerable.Take擴展方法。 您可以使用這些來構建您的新列表。
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];
}
}
}
單元測試:
[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());
}
}
功能
/*
list : the list that is to be rotated
shift : the number of elements to be shifted
direction : direction of shift (1 -> left, -1 -> right)/default left
*/
public List<T> Rotate<T>(List<T> list, int shift, int direction = 1)
{
int j = 0;
List<T> temp_buffer = new List<T>();
if(direction == -1)
{
shift = list.Count - shift;
}
for (int i = 0; i < list.Count; i++)
{
if (i < shift)
{
temp_buffer.Add(list[i]);
}
if (i < list.Count - shift)
{
list[i] = list[i + shift];
}
else
{
list[i] = temp_buffer[j];
j++;
}
}
return list;
}
使用示例
List<int> list = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9};
int shift = 4; //enter the number of elements to be shifted
//printing the list before rotating
Console.Write("Before\t: ");
for(int i = 0; i< list.Count; i++)
{
Console.Write(list[i] + " ");
}
list = Rotate<int>(list, shift, -1);
//printing the list after rotating
Console.Write("\nAfter \t: ");
for(int i = 0; i< list.Count; i++)
{
Console.Write(list[i] + " ");
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.