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

时间:2009-08-19 17:08:16

标签: c# python algorithm

在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 个答案:

答案 0 :(得分:41)

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

答案 1 :(得分:18)

您可以轻松使用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(对于采取)迭代Slice方法的主体。这可以通过迭代列表一次来减少,将项目存储在列表中,然后将列表连接到自身(不需要迭代)。

在这种情况下,这里的权衡是记忆。

为此,我为扩展方法提出以下建议:

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

答案 2 :(得分:9)

C#中最接近的是使用Enumerable.SkipEnumerable.Take扩展方法。您可以使用它们来构建新列表。

答案 3 :(得分:1)

List<int> list1;

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

或者你可以

list2.AddRange(list1);

使用LINQ获取不同的列表

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

答案 4 :(得分:1)

要旋转数组,请执行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];
        }
    }
}

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

答案 5 :(得分:0)

功能

double &GetX()
{
    static double x = 400;
    return x;
}

// We are accessing x here to modify y
// Then we are modifying x itself
// Pass x by reference
double &AccessAndChangeX(double& x)
{
    static double y;
    y = x + 12; // We are accessing x here and using to modify y.

    // Let's modify x
    x = 100;
    return y;
}

void PrintXY(double x, double y)
{
    std::cout << x;
    std::cout << y;
}

int main()
{
    double &x = GetX(); // Take the initial value of x. 400.
    double &y = AccessAndChangeX(x);

    //Print initial value of x and value of y(generated using x)
    PrintXY(x, y);  

    // X was modified while AccessAndChangeX(x). Check if x was changed!
    std::cout << "\n" << "What is the value of x now : " << GetX();
}

用法示例

/*
   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;
}