2009-08-19 24 views
34

Trong python, tôi có thể mất một danh sách my_list và xoay các nội dung:C# tương đương xoay một danh sách sử dụng python lát hoạt động

>>> 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ách tương đương trong C# để tạo một danh sách mới được một tạo thành là gì của hai lát của một danh sách C# hiện có? Tôi biết tôi có thể tạo ra bằng vũ lực nếu cần thiết.

Trả lời

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

Có lẽ nên thêm .ToList() nếu bạn muốn nó tương đương. –

+11

Gọi .ToList() trước khi bạn cần phải là rất xấu cho hiệu suất - nó không phải cái gì tôi muốn khuyến khích bằng cách để lại nằm xung quanh trong mã ví dụ. –

+1

Loại nào là danh sách mới trước .ToList(). Đó là var không giúp dễ đọc :). – pbh101

18

Bạn có thể dễ dàng sử dụng LINQ để làm điều này:

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

Bạn thậm chí có thể thêm này như là một phương pháp khuyến nông như vậy:

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

Tất nhiên cần phải có một số kiểm tra lỗi trong ở trên, nhưng đó là tiền đề chung.


Suy nghĩ về điều này nhiều hơn, có những cải tiến nhất định có thể được thực hiện cho thuật toán này sẽ cải thiện hiệu suất.

Bạn chắc chắn có thể tận dụng lợi thế nếu cá thể IEnumerable<T> triển khai IList<T> hoặc là một mảng, tận dụng thực tế là nó được lập chỉ mục.

Ngoài ra, bạn có thể cắt giảm số lần lặp cần thiết để bỏ qua và thực hiện sẽ nằm trong phần nội dung của thư. Ví dụ, nếu bạn có 200 mục và bạn muốn cắt với giá trị là 199, thì nó yêu cầu 199 (cho lần bỏ qua ban đầu) + 1 (cho mục còn lại) + 199 (cho phép) lặp đi lặp lại trong phần thân của phương thức Slice. Điều này có thể được cắt giảm bằng cách lặp lại thông qua danh sách một lần, lưu trữ các mục trong một danh sách mà sau đó được nối với chính nó (không cần lặp lại).

Trong trường hợp này, giao dịch tắt ở đây là bộ nhớ.

Cuối cùng, tôi đề nghị sau cho phương pháp khuyến nông:

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); 
} 
1
List<int> list1; 

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

hoặc bạn có thể

list2.AddRange(list1); 

Để có được một danh sách riêng biệt sử dụng LINQ

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

Tôi không tìm kiếm một danh sách riêng biệt.Những con số tôi đã cho thấy trong ví dụ của tôi là khác biệt như là một tác dụng phụ của việc sử dụng phạm vi python() cho phép tăng (hoặc giảm) số. – hughdbrown

9

Điều gần nhất trong C# sẽ là sử dụng Enumerable.SkipEnumerable.Take phương pháp mở rộng. Bạn có thể sử dụng chúng để xây dựng danh sách mới của bạn.

1

Để xoay mảng, hãy làm a.Slice(1, null).Concat(a.Slice(null, 1)).

Đây là lỗi của tôi. a.Slice(step: -1) cung cấp bản sao được sao chép là 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]; 
     } 
    } 
} 

kiểm tra nunit:

[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()); 
    } 
} 
Các vấn đề liên quan