【问题标题】:How to Start at Offset and Iterate through Entire List?如何从偏移量开始并遍历整个列表?
【发布时间】:2012-09-23 22:08:37
【问题描述】:

这就是我的清单。

[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开始,循环回到起点,遍历元素回到偏移量。

【问题讨论】:

    标签: c# list circular-buffer


    【解决方案1】:

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

    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
    }
    

    【讨论】:

    • 啊好的谢谢你的名字,什么是更“复杂”的循环缓冲区?只是好奇。
    【解决方案2】:

    这是我的 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 依赖

    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 依赖

    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,或者只需将 ...Should().Be(...) 替换为您的标准 Assert(...) 方法。

    单元测试跨度(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;
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 1970-01-01
      • 2010-12-12
      相关资源
      最近更新 更多