如何使用范围打印出反向数组?

时间:2019-03-16 19:38:04

标签: c# .net range c#-8.0

目前,我正在使用ranges这个新的c# 8.0功能。

到目前为止,我有一个问题。我是否可以从头到尾创建一个range

假设我有一个array,我想通过range反向打印:

static void Test()
{
    int[] array = new int[10];
    for (int i = 0; i < 10; i++) array[i] = i;
    Range range = ^1..0;

    foreach (int v in array[range]) WriteLine(v);
}

但是这给我带来了运行时错误-System.OverflowException: 'Arithmetic operation resulted in an overflow.'我是否正确,ranges只允许一个方向(从开始到结束)?

3 个答案:

答案 0 :(得分:2)

我找不到任何可能的信息。但是,这似乎不是语言限制,因为我们可以创建类似Range r = 5..1;的范围,并且不会引发异常,因此它必须是Array[Range]和其他类型的实现,例如不接受的字符串。这样,您可能可以创建一个接受Range并执行您想要的操作的方法。

编辑:我制作了以下适用于数组的方法:

static T[] MySlice<T>(T[] array, Range r)
{
    //Transforms indexes "from end" to indexes "from start"    
    if(r.Start.ToString()[0] == '^'){
        var startIdx = array.Length - r.Start.Value;
        r = new Range(startIdx,r.End);
    }
    if(r.End.ToString()[0] == '^'){
        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];
}

您可以使用

int[] arr = Enumerable.Range(0,10).ToArray();
Range r = ^0..0;

foreach(var v in MySlice(arr,r)){
    Console.WriteLine(v);
}

哪个输出:

9
8
7
6
5
4
3
2
1
0

编辑: 文档说Index的布尔属性为FromEnd,它指示索引是否来自结尾。在测试期间,我发现实现的属性名称(在dotnet core 3.0.100-preview3-010431中)为IsFromEnd。由于dotnet core 3仍处于测试阶段,因此名称可能仍会更改,因此我坚持使用r.Start.ToString()[0] == '^'来确定Index是否从头开始,直到现在,直到dotnet core 3的最终版本

答案 1 :(得分:2)

反向范围并不是对Range类型的限制,而是对Span(及相关)类型的限制-它们假定为正增量。合理的原因是,数组 slice 的许多低级优化和使用场景仅适用于“指针加大小”的基本概念。 (Link to Range usages in Framework

虽然这不允许传递反向Span,但可以使用扩展方法为反向范围枚举提供简单且 performant 语法:

public static class RangeExtensions
{
    public static int Normalize(this Index index, int length) => 
           index.FromEnd ? 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);

答案 2 :(得分:0)

int[] array = { 1, 2, 3, 4, 5, 6, 7 };
var range = array[0..5];

Console.WriteLine(string.Join(",", range));

var rev = range.AsEnumerable().Reverse();

Console.WriteLine(string.Join(",", rev));

会产生

 1,2,3,4,5
 5,4,3,2,1

我相信这需要C#8.0和System.Linq