mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-22 14:48:47 +00:00
feat: add range-based foreach
This commit is contained in:
parent
bd1b12da71
commit
195e25e0e3
@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- X10D: Added `TextWriter.WriteLineNoAlloc(uint[, ReadOnlySpan<char>[, IFormatProvider]])`.
|
- X10D: Added `TextWriter.WriteLineNoAlloc(uint[, ReadOnlySpan<char>[, IFormatProvider]])`.
|
||||||
- X10D: Added `TextWriter.WriteLineNoAlloc(long[, ReadOnlySpan<char>[, IFormatProvider]])`.
|
- X10D: Added `TextWriter.WriteLineNoAlloc(long[, ReadOnlySpan<char>[, IFormatProvider]])`.
|
||||||
- X10D: Added `TextWriter.WriteLineNoAlloc(ulong[, ReadOnlySpan<char>[, IFormatProvider]])`.
|
- X10D: Added `TextWriter.WriteLineNoAlloc(ulong[, ReadOnlySpan<char>[, IFormatProvider]])`.
|
||||||
|
- X10D: Added `Range.GetEnumerator` (and `RangeEnumerator`), implementing Python-esque `for` loops in C#.
|
||||||
- X10D: Added `string.ConcatIf`.
|
- X10D: Added `string.ConcatIf`.
|
||||||
- X10D: Added `string.MDBold`, `string.MDCode`, `string.MDCodeBlock([string])`, `string.MDHeading(int)`,
|
- X10D: Added `string.MDBold`, `string.MDCode`, `string.MDCodeBlock([string])`, `string.MDHeading(int)`,
|
||||||
`string.MDItalic`, `string.MDLink`, `string.MDStrikeOut`, and `string.MDUnderline` for Markdown formatting.
|
`string.MDItalic`, `string.MDLink`, `string.MDStrikeOut`, and `string.MDUnderline` for Markdown formatting.
|
||||||
|
66
X10D.Tests/src/Core/RangeTests.cs
Normal file
66
X10D.Tests/src/Core/RangeTests.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using X10D.Core;
|
||||||
|
|
||||||
|
namespace X10D.Tests.Core;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
internal class RangeTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void Range_GetEnumerator_ShouldReturnRangeEnumerator()
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(5..10, Is.TypeOf<Range>());
|
||||||
|
Assert.That((5..10).GetEnumerator(), Is.TypeOf<RangeEnumerator>());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Loop_OverRange0To10_ShouldCountFrom0To10Inclusive()
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
foreach (int i in 0..10)
|
||||||
|
{
|
||||||
|
Assert.That(i, Is.EqualTo(value));
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Loop_OverRangeNegative5To5_ShouldCountFromNegative5To5Inclusive()
|
||||||
|
{
|
||||||
|
int value = -5;
|
||||||
|
|
||||||
|
foreach (int i in ^5..5)
|
||||||
|
{
|
||||||
|
Assert.That(i, Is.EqualTo(value));
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Loop_OverRange5ToNegative5_ShouldCountFrom5ToNegative5Inclusive()
|
||||||
|
{
|
||||||
|
int value = 5;
|
||||||
|
|
||||||
|
foreach (int i in 5..^5)
|
||||||
|
{
|
||||||
|
Assert.That(i, Is.EqualTo(value));
|
||||||
|
value--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Loop_OverRange10To0_ShouldCountFrom10To0Inclusive()
|
||||||
|
{
|
||||||
|
int value = 10;
|
||||||
|
|
||||||
|
foreach (int i in 10..0)
|
||||||
|
{
|
||||||
|
Assert.That(i, Is.EqualTo(value));
|
||||||
|
value--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
X10D/src/Core/RangeEnumerator.cs
Normal file
53
X10D/src/Core/RangeEnumerator.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
namespace X10D.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerates the indices of a <see cref="Range" />.
|
||||||
|
/// </summary>
|
||||||
|
public struct RangeEnumerator
|
||||||
|
{
|
||||||
|
private readonly bool _decrement;
|
||||||
|
private readonly int _endValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RangeEnumerator" /> structure.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="range">The range over which to enumerate.</param>
|
||||||
|
public RangeEnumerator(Range range)
|
||||||
|
{
|
||||||
|
Index start = range.Start;
|
||||||
|
Index end = range.End;
|
||||||
|
|
||||||
|
int startValue = start.IsFromEnd ? -start.Value : start.Value;
|
||||||
|
_endValue = end.IsFromEnd ? -end.Value : end.Value;
|
||||||
|
|
||||||
|
_decrement = _endValue < startValue;
|
||||||
|
Current = _decrement ? startValue + 1 : startValue - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the element in the collection at the current position of the enumerator.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The element in the collection at the current position of the enumerator.</value>
|
||||||
|
public int Current { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Advances the enumerator to the next element of the collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if the enumerator was successfully advanced to the next element; <see langword="false" /> if
|
||||||
|
/// the enumerator has passed the end of the collection.
|
||||||
|
/// </returns>
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
int value = Current;
|
||||||
|
|
||||||
|
if (_decrement)
|
||||||
|
{
|
||||||
|
Current--;
|
||||||
|
return value > _endValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Current++;
|
||||||
|
return value < _endValue;
|
||||||
|
}
|
||||||
|
}
|
48
X10D/src/Core/RangeExtensions.cs
Normal file
48
X10D/src/Core/RangeExtensions.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
namespace X10D.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="Range" />.
|
||||||
|
/// </summary>
|
||||||
|
public static class RangeExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Allows the ability to use a <c>for</c> loop to iterate over the indices of a <see cref="Range" />. The indices of the
|
||||||
|
/// range are the inclusive lower and upper bounds of the enumeration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="range">The range whose indices over which will be enumerated.</param>
|
||||||
|
/// <returns>A <see cref="RangeEnumerator" /> that will enumerate over the indices of <paramref name="range" />.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method aims to implement Python-esque for loops in C# by taking advantage of the language syntax used to define
|
||||||
|
/// a <see cref="Range" /> value. Negative bounds may be specified using the C# <c>^</c> operator, or by providing an
|
||||||
|
/// <see cref="Index" /> whose <see cref="Index.IsFromEnd" /> property is <see langword="true" />.
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>
|
||||||
|
/// The following example counts from 0 to 10 inclusive:
|
||||||
|
/// <code>
|
||||||
|
/// foreach (var i in 0..10)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine(i);
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
///
|
||||||
|
/// To use negative bounds, use the <c>^</c> operator. The following example counts from -5 to 5 inclusive:
|
||||||
|
/// <code>
|
||||||
|
/// foreach (var i in ^5..5)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine(i);
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
///
|
||||||
|
/// Decrementing enumeration is supported. The following example counts from 5 to -5 inclusive:
|
||||||
|
/// <code>
|
||||||
|
/// foreach (var i in 5..^5)
|
||||||
|
/// {
|
||||||
|
/// Console.WriteLine(i);
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
public static RangeEnumerator GetEnumerator(this Range range)
|
||||||
|
{
|
||||||
|
return new RangeEnumerator(range);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user