如何从Offset开始并遍历整个列表?

时间:2012-09-23 22:08:38

标签: c# list circular-buffer

所以这就是我的名单。

[0], [1], [2], [3], [4]

我希望能够遍历这些 - 但这里的技巧是我想从一个偏移开始然后循环回到那个偏移量?

离。

[0], [1], [2], [3], [4]
      o-->            
//Start at offset 1 then get 2, 3, 4 then loop back around to zero

EX2。

[0], [1], [2], [3], [4]
                o-->            
//Start at offset 3 then get 4, then loop back around to zero, then 1, 2 

我考虑使用常规List<T>并尝试将此概念实现为for循环,但我不确定是否要这样做,如果他们更简洁的方式这样做。

基本上不要从0开始并循环回到开始并将元素返回到偏移量。

2 个答案:

答案 0 :(得分:6)

您实际上是在描述环形缓冲区或循环缓冲区。

http://en.wikipedia.org/wiki/Circular_buffer

简单的实现是

int start; // Set your desired start offset

for (int i = start; i < myList.Length; i++)
{
    // do stuff
}

for (int j = 0; j < start; j++)
{
    // do stuff
}

答案 1 :(得分:0)

这是我的C#实现使用扩展方法(我认为)我的所有依赖项。

我知道这似乎有很多代码可以发布这么简单的事情,但它主要是验证代码,它在我写的应用程序中非常关键,可以完美地运行良好定义的行为。在这两个函数之上构建了许多其他方法。

以这种方式迭代的性能似乎也很好,它相对于应用于迭代值的操作从未成为瓶颈。

Span和SpanRange扩展方法

using System;
using System.Collections.Generic;
using Common.FluentValidation;

namespace Common.Extensions
{
    public static partial class ExtensionMethods
    {
        /// <summary>
        /// Gets the index for an array relative to an anchor point, seamlessly crossing array boundaries in either direction.
        /// Returns calculated index value of an element within a collection as if the collection was a ring of contiguous elements (Ring Buffer).
        /// </summary>
        /// <param name="p_rollover">Index value after which the iterator should return back to zero.</param>
        /// <param name="p_anchor">A fixed or variable position to offset the iteration from.</param>
        /// <param name="p_offset">A fixed or variable position to offset from the anchor.</param>
        /// <returns>calculated index value of an element within a collection as if the collection was a ring of contiguous elements (Ring Buffer).</returns>
        public static int Span(this int p_rollover, int p_anchor, int p_offset)
        {
            // Prevent absolute value of `n` from being larger than count
            int n = (p_anchor + p_offset) % p_rollover;

            // If `n` is negative, then result is n less than rollover
            if (n < 0)
                n = n + p_rollover;

            return n;
        }

        /// <summary>
        /// Iterates over a collection from a specified start position to an inclusive end position. Iterator always increments
        /// from start to end, treating the original collection as a contiguous ring of items to be projected into a new form.
        /// Returns a projected collection of items that can contain all of the items from the original collection or a subset of the items.
        /// The first item in the projected collection will be the item from the original collection at index position p_first.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_collection">The collection to project into a new form.</param>
        /// <param name="p_first">Zero based index value of the first element in the projected sequence.</param>
        /// <param name="p_last">Zero based index value of the last element in the projected sequence.</param>
        /// <returns>a projected collection of items that can contain all of the items from the original collection or a subset of the items.
        /// The first item in the projected collection will be the item from the original collection at index position p_first.</returns>
        public static IEnumerable<T> SpanRange<T>(this IList<T> p_collection, int p_first, int p_last)
        {
            // Validate
            p_collection
                .CannotBeNullOrEmpty("p_collection");
            p_first
                .MustBeWithinRange(0, p_collection.Count - 1, "p_first");
            p_last
                .MustBeWithinRange(0, p_collection.Count - 1, "p_last");

            // Init
            int Rollover = p_collection.Count;
            int Count = (p_first <= p_last) ? p_last - p_first : (Rollover - p_first) + p_last;

            // Iterate
            for (int i = 0; i <= Count; i++)
            {
                var n = Rollover.Span(p_first, i);
                yield return p_collection[n];
            }
        }
    }
}

CannotBeNullOrEmpty Dependancy

using System;
using System.Collections.Generic;
using System.Linq;

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(string.Format(
                        "The collection \"{0}\" cannot be null.",
                        p_name), default(Exception));

            if (p_parameter.Count <= 0)
                throw
                    new
                        ArgumentOutOfRangeException(string.Format(
                        "The collection \"{0}\" cannot be empty.",
                        p_name), default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this IEnumerable<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(string.Format(
                        "The collection \"{0}\" cannot be null.",
                        p_name), default(Exception));

            if (p_parameter.Count() <= 0)
                throw
                    new
                        ArgumentOutOfRangeException(string.Format(
                        "The collection \"{0}\" cannot be empty.",
                        p_name), default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentException"></exception>
        public static void CannotBeNullOrEmpty(this string p_parameter, string p_name)
        {
            if (string.IsNullOrEmpty(p_parameter))
                throw
                    new
                        ArgumentException(string.Format(
                        "The string \"{0}\" cannot be null or empty.",
                        p_name), default(Exception));
        }
    }
}

MustBeWithinRange Dependancy

using System;

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is within a specified range of values, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_minInclusive">The minimum valid value of the parameter.</param>
        /// <param name="p_maxInclusive">The maximum valid value of the parameter.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void MustBeWithinRange<T>(this IComparable<T> p_parameter, T p_minInclusive, T p_maxInclusive, string p_name)
            where T : struct
        {
            if (p_parameter.CompareTo(p_minInclusive) == -1)
                throw
                    new
                        ArgumentOutOfRangeException(string.Format(
                        "Parameter cannot be less than {0}, but \"{1}\" was {2}.",
                        p_minInclusive, p_name, p_parameter), default(Exception));

            if (p_parameter.CompareTo(p_maxInclusive) == 1)
                throw
                    new
                        ArgumentOutOfRangeException(string.Format(
                        "Parameter cannot be greater than {0}, but \"{1}\" was {2}.",
                        p_maxInclusive, p_name, p_parameter), default(Exception));
        }
    }
}

注意:包含我的单元测试依赖项的代码太多了,所以如果你有自己类似的扩展方法,或者只是注释掉像我这样的东西,那么希望测试用例名称的详细程度可以解决问题。 Print()语句,它以半智能方式将所有值转储到StringBuilder。您将需要NuGet提供的FluentValidation,或者只使用您的标准Assert(...)方法替换... Should()。Be(...)。

单元测试跨度(NUnit框架)

using System;
using System.Text;
using Common.Extensions;
using Common.FluentValidation;
using FluentAssertions;
using NUnit.Framework;

namespace UnitTests.CommonTests.Extensions
{
    [TestFixture, Timeout(1000)]
    public class Span_Tests
    {
        // Test Anchoring
        [Test]
        public void Span_10_anchored_at_0_with_no_offset_should_be_0_Tests()
        {
            10.Span(0, 0).Should().Be(0);
        }

        [Test]
        public void Span_10_anchored_at_1_with_no_offset_should_be_1_Tests()
        {
            10.Span(1, 0).Should().Be(1);
        }

        [Test]
        public void Span_10_anchored_at_negative_1_with_no_offset_should_be_9_Tests()
        {
            10.Span(-1, 0).Should().Be(9);
        }

        [Test]
        public void Span_10_anchored_at_negative_10_with_no_offset_should_be_0_Tests()
        {
            10.Span(-10, 0).Should().Be(0);
        }

        // Test Offset
        [Test]
        public void Span_10_anchored_at_0_with_offset_of_1_should_be_1_Tests()
        {
            10.Span(0, 1).Should().Be(1);
        }

        [Test]
        public void Span_10_anchored_at_0_with_offset_of_negative_1_should_be_9_Tests()
        {
            10.Span(0, -1).Should().Be(9);
        }

        [Test]
        public void Span_10_anchored_at_0_with_offset_of_negative_10_should_be_0_Tests()
        {
            10.Span(0, -10).Should().Be(0);
        }

        // Test Iterations
        [Test]
        public void Span_array_of_10_anchored_at_0_can_walk_forward_thru_elements_twice_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var SequentialData = new int[10];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());

                for (int i = 0; i < 20; i++)
                {
                    var n = SequentialData.Length.Span(0, i);

                    SequentialData[n].Should().Be(i % 10);
                }
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void Span_array_of_10_anchored_at_0_can_walk_backwards_thru_elements_twice_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var SequentialData = new int[10];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());

                for (int i = 0; i > -20; i--)
                {
                    var n = SequentialData.Length.Span(0, i);
                    var j = (i % 10);

                    switch (j)
                    {
                        case 0: SequentialData[n].Should().Be(0); break;
                        case -1: SequentialData[n].Should().Be(9); break;
                        case -2: SequentialData[n].Should().Be(8); break;
                        case -3: SequentialData[n].Should().Be(7); break;
                        case -4: SequentialData[n].Should().Be(6); break;
                        case -5: SequentialData[n].Should().Be(5); break;
                        case -6: SequentialData[n].Should().Be(4); break;
                        case -7: SequentialData[n].Should().Be(3); break;
                        case -8: SequentialData[n].Should().Be(2); break;
                        case -9: SequentialData[n].Should().Be(1); break;

                        default:
                            throw
                                j.ToString().CannotBeSwitchedToDefault("j");
                    }
                }
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }
    }
}

单元测试SpanRange(NUnit框架)

using System;
using System.Collections.Generic;
using System.Text;
using Common.Extensions;
using FluentAssertions;
using NUnit.Framework;

namespace UnitTests.CommonTests.Extensions
{
    [TestFixture, Timeout(1000)]
    public class SpanRange_Tests
    {
        [Test]
        public void SpanRange_array_of_10_from_0_to_9_should_be_0_thru_9_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

                var SequentialData = new int[10];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(0, 9));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_10_from_0_to_9_should_not_throw_ArgumentOutOfRangeException_Tests()
        {
            foreach (var item in new int[10].SpanRange(0, 9))
            { }
        }

        [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void SpanRange_array_of_10_from_0_to_10_should_throw_ArgumentOutOfRangeException_Tests()
        {
            foreach (var item in new int[10].SpanRange(0, 10))
            { }
        }

        [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void SpanRange_array_of_10_from_negative_1_to_9_should_throw_ArgumentOutOfRangeException_Tests()
        {
            foreach (var item in new int[10].SpanRange(-1, 9))
            { }
        }

        [Test]
        public void SpanRange_array_of_10_from_1_to_0_should_be_1_thru_9_then_0_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

                var SequentialData = new int[10];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(1, 0));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_10_from_1_to_3_should_be_1_thru_3_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 1, 2, 3 };

                var SequentialData = new int[10];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(1, 3));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_10_from_9_to_1_should_be_9_0_and_1_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 9, 0, 1 };

                var SequentialData = new int[10];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(9, 1));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_5_from_1_to_4_should_be_1_thru_4_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 1, 2, 3, 4 };

                var SequentialData = new int[5];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(1, 4));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_5_from_0_to_0_should_be_0_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 0 };

                var SequentialData = new int[5];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(0, 0));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_5_from_1_to_1_should_be_1_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 1 };

                var SequentialData = new int[5];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(1, 1));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_5_from_4_to_0_should_be_4_then_0_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 4, 0 };

                var SequentialData = new int[5];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(4, 0));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }

        [Test]
        public void SpanRange_array_of_5_from_1_to_0_should_be_1_thru_4_then_0_Tests()
        {
            var sb = new StringBuilder();

            try
            {
                var Expected = new List<int>() { 1, 2, 3, 4, 0 };

                var SequentialData = new int[5];
                for (int i = 0; i < SequentialData.Length; i++)
                    SequentialData[i] = i;

                sb.AppendFormat("SequentialData:\r\n{0}", SequentialData.Print());
                sb.AppendLine();

                var Range = new List<int>(SequentialData.SpanRange(1, 0));

                sb.AppendFormat("Range:\r\n{0}", Range.Print());

                Range.ShouldBeEquivalentTo(Expected);
            }
            catch (Exception)
            {
                Console.WriteLine(sb.ToString());
                throw;
            }
        }
    }
}