How to print out a reversed array using range?
Solution 1:
Reverse ranges are not so much a limitation on the Range
type, but rather a limitation on the Span
(and related) types – which assume a positive increment. The rational is that many of the low-level optimizations and usage scenarios for a slice of an array are only geared toward the basic notion of a "pointer plus size". (Link to Range usages in Framework)
While this won't allow a reverse Span
to be passed around, an extension method could be used to provide an easy and performant syntax for reverse range enumeration:
public static class RangeExtensions
{
public static int Normalize(this Index index, int length) =>
index.IsFromEnd ? length - index.Value : index.Value;
public static (int start, int end) Normalize(this Range range, int length) =>
(range.Start.Normalize(length), range.End.Normalize(length));
public static IEnumerable<T> Enumerate<T>(this T[] items, Range range)
{
var (start, end) = range.Normalize(items.Length);
return start <= end ? items[range] : GetRangeReverse();
IEnumerable<T> GetRangeReverse()
{
for (int i = start; i >= end; i--)
yield return items[i];
}
}
}
...
var numbers = Enumerable.Range(0, 10).ToArray();
foreach (var num in numbers.Enumerate(^3..1))
Console.WriteLine(num);
Solution 2:
I couldn't find anything that says that's possible. But, it doesn't appear to be a language restriction, as we can create a range like Range r = 5..1;
and that doesn't throw exceptions, so it must be the implementation of Array[Range]
and other types, like string that doesn't accept it. That put, you can probably create an method that accepts a Range
and does what you want.
Edit: I made the following method that works with arrays:
static T[] MySlice<T>(T[] array, Range r)
{
//Transforms indexes "from end" to indexes "from start"
if(r.Start.IsFromEnd){
var startIdx = array.Length - r.Start.Value;
r = new Range(startIdx,r.End);
}
if(r.End.IsFromEnd){
var endIdx = array.Length - r.End.Value;
r = new Range(r.Start,endIdx);
}
//Check if start value is greater than end value. If so, invert it
if(r.Start.Value > r.End.Value)
{
r = new Range(r.End,r.Start);
var invArr = array[r];
Array.Reverse(invArr);
return invArr;
}
return array[r];
}
And you can use like
int[] arr = Enumerable.Range(0,10).ToArray();
Range r = ^0..0;
foreach(var v in MySlice(arr,r)){
Console.WriteLine(v);
}
Which outputs:
9
8
7
6
5
4
3
2
1
0