简体   繁体   English

获取未指定长度列表的范围范围?

[英]Get Range of a Range for a list of unspecified length?

I'm trying to build up a library of extension methods and would like to integrate using Range objects into it where appropriate.我正在尝试建立一个扩展方法库,并希望在适当的时候使用Range对象将其集成到其中。

So I've created a class to represent a lightweight view into any arbitrary list and is defined in terms of a given Range .因此,我创建了一个 class 来表示任意列表的轻量级视图,并根据给定的Range进行定义。

public static class ListSlice
{
    public static IReadOnlyList<T> Create<T>(IReadOnlyList<T> list, Range range) => new ListSlice<T>(list, range);
    public static IReadOnlyList<T> Create<T>(IReadOnlyList<T> list, Index start, Index end) => Create(list, new Range(start, end));
}

public class ListSlice<T> : IReadOnlyList<T>
{
    public ListSlice(IReadOnlyList<T> list, Range range)
    {
        (List, Range) = list is ListSlice<T> slice
            ? (slice.List, slice.Range.Apply(range)) // range of range
            : (list, range);
    }
    public IReadOnlyList<T> List { get; }
    public Range Range { get; }

    public T this[int index] => List[Range.GetOffsetAndLength(List.Count).Offset + index];
    public int Count => Range.GetOffsetAndLength(List.Count).Length;

    public IEnumerator<T> GetEnumerator()
    {
        var (offset, length) = Range.GetOffsetAndLength(List.Count);
        foreach (var i in Enumerable.Range(0, length))
            yield return List[offset + i];
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

There is one corner case where I want to get a slice of a slice.有一个极端情况,我想得到一片。 I could work out the math to calculate the new effective range given the length of the original list... but is it even possible just looking at ranges alone?给定原始列表的长度,我可以算出计算新有效范围的数学方法……但是否有可能只看范围?

As far as I can see, it's pretty straight forward when the ranges have definite starts and ends.据我所知,当范围有明确的开始和结束时,它非常简单。 But I'm having trouble visualizing how it would work when dealing with ranges that have starts or ends that are relative to the end.但是在处理具有相对于结束的开始或结束的范围时,我无法想象它是如何工作的。 How do I do the math for this?我该如何计算呢?

eg,例如,

// absolute start/end, length is immediately known, difference between the two
0..20    // len: 20
5..10    // len: 5
5..15    // len: 10

// if both starts and ends relative to end, length is still the difference
^20..^0  // len: 20
^15..^10 // len: 5
^15..^5  // len: 10

// how do I get the length if it's open ended?
 0..^10  // len: ???
^5..20   // len: ???
 0..     // len: ??? (equivalent to: 0..^1)

I thought maybe using an arbitrary length as a stand in and go from there, I feel like I'm missing some cases.我想也许可以使用任意长度作为替代,然后从那里使用 go,我觉得我遗漏了一些案例。

public static class RangeExtensions
{
    public static Range Apply(this Range range, int baseLength, Range subRange)
    {
        var (offset1, length1) = range.GetOffsetAndLength(baseLength);
        var (offset2, length2) = subRange.GetOffsetAndLength(length1);
        return (offset1 + offset2)..(offset1 + offset2 + length2);
    }

    public static Range Apply(this Range range, Range subRange)
    {
        // easy case: (5..10)(..2) -> (5..7)
        // weird case: (5..10)(^2..) -> ???
        var dummyLength = 100; // not sure about this
        var (offset1, length1) = range.GetOffsetAndLength(dummyLength);
        var (offset2, length2) = subRange.GetOffsetAndLength(length1);
        var offset = offset1 + offset2;
        return offset..(offset + length2); // somehow have to offset the dummyLength
    }
}

Like you've correctly identified, if IsFromEnd match, then you can easily find the result by adding the Value s.就像您已经正确识别一样,如果IsFromEnd匹配,那么您可以通过添加Value s 轻松找到结果。 In the other branches you have to subtract the inner offset instead, and set IsFromEnd accordingly.在其他分支中,您必须改为减去inner偏移量,并相应地设置IsFromEnd Sketching each case on paper helps.在纸上画出每个案例会有所帮助。

Note: In your other code, you may want to consider the case where a range's start falls after its end.注意:在您的其他代码中,您可能需要考虑范围的起点落在其终点之后的情况。

public static Range Within(this Range inner, Range outer)
{
    Index start;

    if (inner.Start.IsFromEnd)
    {
        if (outer.End.IsFromEnd)
            start = ^(outer.End.Value + inner.Start.Value);
        else
            start = outer.End.Value - inner.Start.Value;

    }
    else
    {
        if (outer.Start.IsFromEnd)
            start = ^(outer.Start.Value - inner.Start.Value);
        else
            start = outer.Start.Value + inner.Start.Value;
    }

    Index end;

    if (inner.End.IsFromEnd)
    {
        if (outer.End.IsFromEnd)
            end = ^(outer.End.Value + inner.End.Value);
        else
            end = outer.End.Value - inner.End.Value;
    }
    else
    {
        if (outer.Start.IsFromEnd)
            end = ^(outer.Start.Value - inner.End.Value);
        else
            end = outer.Start.Value + inner.End.Value;
    }

    return start..end;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM