Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What Does The relative(to:) Function Actually Do?

This is from the Swift Standard Library Documentation:

relative(to:)

Returns the range of indices within the given collection described by this range expression.

Here is the method signature:

func relative<C>(to collection: C) -> Range<Self.Bound> where C : _Indexable, Self.Bound == C.Index

Along with its explanation:

Parameters

collection

The collection to evaluate this range expression in relation to.

Return Value

A range suitable for slicing collection. The returned range is not guaranteed to be inside the bounds of collection. Callers should apply the same preconditions to the return value as they would to a range provided directly by the user.

Finally, here is my test code:

let continuousCollection = Array(0..<10)
var range = 0..<5
print(range.relative(to: continuousCollection))
//0..<5
range = 5..<15
print(range.relative(to: continuousCollection))
//5..<15
range = 11..<15
print(range.relative(to: continuousCollection))
//11..<15
let disparateCollection = [1, 4, 6, 7, 10, 12, 13, 16, 18, 19, 22]
range = 0..<5
print(range.relative(to: disparateCollection))
//0..<5
range = 5..<15
print(range.relative(to: disparateCollection))
//5..<15
range = 11..<15
print(range.relative(to: disparateCollection))
//11..<15

In every case, relative(to:) just returns the original range. What is this method supposed to do?

like image 330
Brandon Bradley Avatar asked Mar 09 '23 12:03

Brandon Bradley


1 Answers

relative(to:) is a requirement of the RangeExpression protocol, which Swift's range types conform to in Swift 4.

This includes:

  • Half-open ranges created with the infix ..< operator (Range & CountableRange)
  • Closed ranges created with the infix ... operator (ClosedRange & CountableClosedRange)
  • The partial ranges introduced in SE-0172, including:
    • Left-handed ranges created with the postfix ... operator (PartialRangeFrom & CountablePartialRangeFrom)
    • Closed right-handed ranges created with the prefix ... operator (PartialRangeUpTo)
    • Half-open right-handed ranges created with the prefix ..< operator (PartialRangeThrough)

As the documentation states, calling relative(to:) on a range expression with a given Collection (where the range has bounds that match the Index type of the collection) returns a Range suitable for slicing that collection.

In the case of half-open ranges, the bounds just stay the same, as you've observed. However, the results will differ with other range types. For example, with closed ranges, the upper bound will need to be incremented (as it's no longer inclusive). With partial ranges, the missing lower or upper bounds need to be "filled in" by the collection's startIndex or endIndex respectively.

For example:

let continuousCollection = Array(0 ..< 10)

do {
    let range = 0 ..< 5 // CountableRange
    print(range.relative(to: continuousCollection)) // 0..<5
}

do {
    let range = 0 ... 5 // ClosedCountableRange
    print(range.relative(to: continuousCollection)) // 0..<6
}

do {
    let range = 4... // CountablePartialRangeFrom
    print(range.relative(to: continuousCollection)) // 4..<10
}

do {
    let range = ..<9 // PartialRangeUpTo
    print(range.relative(to: continuousCollection)) // 0..<9
}

do {
    let range = ...3 // PartialRangeThrough
    print(range.relative(to: continuousCollection)) // 0..<4
}

The relative(to:) requirement of RangeExpression then allows the standard library, amongst other things, to write a generic ranged subscript on Collection, allowing an arbitrary collection to be subscripted with an arbitrary range type with Index bound(s):

let continuousCollection = Array(0 ..< 10)

print(continuousCollection[0 ..< 5]) // [0, 1, 2, 3, 4]
print(continuousCollection[0 ... 5]) // [0, 1, 2, 3, 4, 5]
print(continuousCollection[4...]) // [4, 5, 6, 7, 8, 9]
print(continuousCollection[..<9]) // [0, 1, 2, 3, 4, 5, 6, 7, 8]
print(continuousCollection[...3]) // [0, 1, 2, 3]
like image 77
Hamish Avatar answered Mar 20 '23 07:03

Hamish