Skip to content

Commit 8e02cbc

Browse files
natecook1000airspeedswift
authored andcommitted
[stdlib] Update complexity docs for seq/collection algorithms (#17254) (#18247)
* [stdlib] Update complexity docs for seq/collection algorithms This corrects and standardizes the complexity documentation for Sequence and Collection methods. The use of constants is more consistent, with `n` equal to the length of the target collection, `m` equal to the length of a collection passed in as a parameter, and `k` equal to any other passed or calculated constant. * Apply notes from @brentdax about complexity nomenclature * Change `n` to `distance` in `index(_:offsetBy:)` * Use equivalency language more places; sync across array types * Use k instead of n for parameter names * Slight changes to index(_:offsetBy:) discussion. * Update tests with new parameter names
1 parent 40245f2 commit 8e02cbc

13 files changed

+465
-329
lines changed

stdlib/public/core/Arrays.swift.gyb

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -619,24 +619,25 @@ extension ${Self}: RandomAccessCollection, MutableCollection {
619619
/// print(numbers[i])
620620
/// // Prints "50"
621621
///
622-
/// The value passed as `n` must not offset `i` beyond the bounds of the
623-
/// collection.
622+
/// The value passed as `distance` must not offset `i` beyond the bounds of
623+
/// the collection.
624624
///
625625
/// - Parameters:
626626
/// - i: A valid index of the array.
627-
/// - n: The distance to offset `i`.
628-
/// - Returns: An index offset by `n` from the index `i`. If `n` is positive,
629-
/// this is the same value as the result of `n` calls to `index(after:)`.
630-
/// If `n` is negative, this is the same value as the result of `-n` calls
631-
/// to `index(before:)`.
632-
@inlinable
633-
public func index(_ i: Int, offsetBy n: Int) -> Int {
627+
/// - distance: The distance to offset `i`.
628+
/// - Returns: An index offset by `distance` from the index `i`. If
629+
/// `distance` is positive, this is the same value as the result of
630+
/// `distance` calls to `index(after:)`. If `distance` is negative, this
631+
/// is the same value as the result of `abs(distance)` calls to
632+
/// `index(before:)`.
633+
@inlinable
634+
public func index(_ i: Int, offsetBy distance: Int) -> Int {
634635
// NOTE: this is a manual specialization of index movement for a Strideable
635636
// index that is required for Array performance. The optimizer is not
636637
// capable of creating partial specializations yet.
637638
// NOTE: Range checks are not performed here, because it is done later by
638639
// the subscript function.
639-
return i + n
640+
return i + distance
640641
}
641642

642643
/// Returns an index that is the specified distance from the given index,
@@ -665,33 +666,36 @@ extension ${Self}: RandomAccessCollection, MutableCollection {
665666
/// print(j)
666667
/// // Prints "nil"
667668
///
668-
/// The value passed as `n` must not offset `i` beyond the bounds of the
669-
/// collection, unless the index passed as `limit` prevents offsetting
669+
/// The value passed as `distance` must not offset `i` beyond the bounds of
670+
/// the collection, unless the index passed as `limit` prevents offsetting
670671
/// beyond those bounds.
671672
///
672673
/// - Parameters:
673674
/// - i: A valid index of the array.
674-
/// - n: The distance to offset `i`.
675-
/// - limit: A valid index of the collection to use as a limit. If `n > 0`,
676-
/// `limit` has no effect if it is less than `i`. Likewise, if `n < 0`,
677-
/// `limit` has no effect if it is greater than `i`.
678-
/// - Returns: An index offset by `n` from the index `i`, unless that index
679-
/// would be beyond `limit` in the direction of movement. In that case,
680-
/// the method returns `nil`.
675+
/// - distance: The distance to offset `i`.
676+
/// - limit: A valid index of the collection to use as a limit. If
677+
/// `distance > 0`, `limit` has no effect if it is less than `i`.
678+
/// Likewise, if `distance < 0`, `limit` has no effect if it is greater
679+
/// than `i`.
680+
/// - Returns: An index offset by `distance` from the index `i`, unless that
681+
/// index would be beyond `limit` in the direction of movement. In that
682+
/// case, the method returns `nil`.
683+
///
684+
/// - Complexity: O(1)
681685
@inlinable
682686
public func index(
683-
_ i: Int, offsetBy n: Int, limitedBy limit: Int
687+
_ i: Int, offsetBy distance: Int, limitedBy limit: Int
684688
) -> Int? {
685689
// NOTE: this is a manual specialization of index movement for a Strideable
686690
// index that is required for Array performance. The optimizer is not
687691
// capable of creating partial specializations yet.
688692
// NOTE: Range checks are not performed here, because it is done later by
689693
// the subscript function.
690694
let l = limit - i
691-
if n > 0 ? l >= 0 && l < n : l <= 0 && n < l {
695+
if distance > 0 ? l >= 0 && l < distance : l <= 0 && distance < l {
692696
return nil
693697
}
694-
return i + n
698+
return i + distance
695699
}
696700

697701
/// Returns the distance between two indices.
@@ -735,12 +739,15 @@ extension ${Self}: RandomAccessCollection, MutableCollection {
735739
/// - Parameter index: The position of the element to access. `index` must be
736740
/// greater than or equal to `startIndex` and less than `endIndex`.
737741
///
742+
%if Self == 'ContiguousArray':
738743
/// - Complexity: Reading an element from an array is O(1). Writing is O(1)
739744
/// unless the array's storage is shared with another array, in which case
740745
/// writing is O(*n*), where *n* is the length of the array.
741-
%if Self == 'Array':
742-
/// If the array uses a bridged `NSArray` instance as its storage, the
743-
/// efficiency is unspecified.
746+
%else:
747+
/// - Complexity: Reading an element from an array is O(1). Writing is O(1)
748+
/// unless the array's storage is shared with another array or uses a
749+
/// bridged `NSArray` instance as its storage, in which case writing is
750+
/// O(*n*), where *n* is the length of the array.
744751
%end
745752
@inlinable
746753
public subscript(index: Int) -> Element {
@@ -1416,9 +1423,8 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol {
14161423
///
14171424
/// - Parameter newElement: The element to append to the array.
14181425
///
1419-
/// - Complexity: Amortized O(1) over many additions. If the array uses a
1420-
/// bridged `NSArray` instance as its storage, the efficiency is
1421-
/// unspecified.
1426+
/// - Complexity: O(1) on average, over many calls to `append(_:)` on the
1427+
/// same array.
14221428
@inlinable
14231429
@_semantics("array.append_element")
14241430
public mutating func append(_ newElement: Element) {
@@ -1441,7 +1447,9 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol {
14411447
///
14421448
/// - Parameter newElements: The elements to append to the array.
14431449
///
1444-
/// - Complexity: O(*n*), where *n* is the length of the resulting array.
1450+
/// - Complexity: O(*m*) on average, where *m* is the length of
1451+
/// `newElements`, over many calls to `append(contentsOf:)` on the same
1452+
/// array.
14451453
@inlinable
14461454
@_semantics("array.append_contentsOf")
14471455
public mutating func append<S : Sequence>(contentsOf newElements: S)
@@ -1588,7 +1596,8 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol {
15881596
/// `index` must be a valid index of the array or equal to its `endIndex`
15891597
/// property.
15901598
///
1591-
/// - Complexity: O(*n*), where *n* is the length of the array.
1599+
/// - Complexity: O(*n*), where *n* is the length of the array. If
1600+
/// `i == endIndex`, this method is equivalent to `append(_:)`.
15921601
@inlinable
15931602
public mutating func insert(_ newElement: Element, at i: Int) {
15941603
_checkIndex(i)
@@ -1943,9 +1952,10 @@ extension ${Self} {
19431952
/// a subrange must be valid indices of the array.
19441953
/// - newElements: The new elements to add to the array.
19451954
///
1946-
/// - Complexity: O(`subrange.count`) if you are replacing a suffix of the
1947-
/// array with an empty collection; otherwise, O(*n*), where *n* is the
1948-
/// length of the array.
1955+
/// - Complexity: O(*n* + *m*), where *n* is length of the array and
1956+
/// *m* is the length of `newElements`. If the call to this method simply
1957+
/// appends the contents of `newElements` to the array, this method is
1958+
/// equivalent to `append(contentsOf:)`.
19491959
@inlinable
19501960
@_semantics("array.mutate_unknown")
19511961
public mutating func replaceSubrange<C>(

stdlib/public/core/BidirectionalCollection.swift

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ where SubSequence: BidirectionalCollection, Indices: BidirectionalCollection {
235235
///
236236
/// - Parameter bounds: A range of the collection's indices. The bounds of
237237
/// the range must be valid indices of the collection.
238+
///
239+
/// - Complexity: O(1)
238240
subscript(bounds: Range<Index>) -> SubSequence { get }
239241

240242
// FIXME(ABI): Associated type inference requires this.
@@ -257,26 +259,26 @@ extension BidirectionalCollection {
257259
}
258260

259261
@inlinable // FIXME(sil-serialize-all)
260-
public func index(_ i: Index, offsetBy n: Int) -> Index {
261-
if n >= 0 {
262-
return _advanceForward(i, by: n)
262+
public func index(_ i: Index, offsetBy distance: Int) -> Index {
263+
if distance >= 0 {
264+
return _advanceForward(i, by: distance)
263265
}
264266
var i = i
265-
for _ in stride(from: 0, to: n, by: -1) {
267+
for _ in stride(from: 0, to: distance, by: -1) {
266268
formIndex(before: &i)
267269
}
268270
return i
269271
}
270272

271273
@inlinable // FIXME(sil-serialize-all)
272274
public func index(
273-
_ i: Index, offsetBy n: Int, limitedBy limit: Index
275+
_ i: Index, offsetBy distance: Int, limitedBy limit: Index
274276
) -> Index? {
275-
if n >= 0 {
276-
return _advanceForward(i, by: n, limitedBy: limit)
277+
if distance >= 0 {
278+
return _advanceForward(i, by: distance, limitedBy: limit)
277279
}
278280
var i = i
279-
for _ in stride(from: 0, to: n, by: -1) {
281+
for _ in stride(from: 0, to: distance, by: -1) {
280282
if i == limit {
281283
return nil
282284
}
@@ -317,7 +319,7 @@ extension BidirectionalCollection where SubSequence == Self {
317319
/// - Returns: The last element of the collection if the collection has one
318320
/// or more elements; otherwise, `nil`.
319321
///
320-
/// - Complexity: O(1).
322+
/// - Complexity: O(1)
321323
@inlinable // FIXME(sil-serialize-all)
322324
public mutating func popLast() -> Element? {
323325
guard !isEmpty else { return nil }
@@ -344,20 +346,20 @@ extension BidirectionalCollection where SubSequence == Self {
344346

345347
/// Removes the given number of elements from the end of the collection.
346348
///
347-
/// - Parameter n: The number of elements to remove. `n` must be greater
349+
/// - Parameter k: The number of elements to remove. `k` must be greater
348350
/// than or equal to zero, and must be less than or equal to the number of
349351
/// elements in the collection.
350352
///
351353
/// - Complexity: O(1) if the collection conforms to
352-
/// `RandomAccessCollection`; otherwise, O(*n*), where *n* is the length
353-
/// of the collection.
354+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the number of
355+
/// elements to remove.
354356
@inlinable // FIXME(sil-serialize-all)
355-
public mutating func removeLast(_ n: Int) {
356-
if n == 0 { return }
357-
_precondition(n >= 0, "Number of elements to remove should be non-negative")
358-
_precondition(count >= n,
357+
public mutating func removeLast(_ k: Int) {
358+
if k == 0 { return }
359+
_precondition(k >= 0, "Number of elements to remove should be non-negative")
360+
_precondition(count >= k,
359361
"Can't remove more items from a collection than it contains")
360-
self = self[startIndex..<index(endIndex, offsetBy: -n)]
362+
self = self[startIndex..<index(endIndex, offsetBy: -k)]
361363
}
362364
}
363365

@@ -374,18 +376,20 @@ extension BidirectionalCollection {
374376
/// print(numbers.dropLast(10))
375377
/// // Prints "[]"
376378
///
377-
/// - Parameter n: The number of elements to drop off the end of the
378-
/// collection. `n` must be greater than or equal to zero.
379-
/// - Returns: A subsequence that leaves off `n` elements from the end.
379+
/// - Parameter k: The number of elements to drop off the end of the
380+
/// collection. `k` must be greater than or equal to zero.
381+
/// - Returns: A subsequence that leaves off `k` elements from the end.
380382
///
381-
/// - Complexity: O(*n*), where *n* is the number of elements to drop.
383+
/// - Complexity: O(1) if the collection conforms to
384+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the number of
385+
/// elements to drop.
382386
@inlinable // FIXME(sil-serialize-all)
383-
public func dropLast(_ n: Int) -> SubSequence {
387+
public func dropLast(_ k: Int) -> SubSequence {
384388
_precondition(
385-
n >= 0, "Can't drop a negative number of elements from a collection")
389+
k >= 0, "Can't drop a negative number of elements from a collection")
386390
let end = index(
387391
endIndex,
388-
offsetBy: -n,
392+
offsetBy: -k,
389393
limitedBy: startIndex) ?? startIndex
390394
return self[startIndex..<end]
391395
}
@@ -407,7 +411,9 @@ extension BidirectionalCollection {
407411
/// - Returns: A subsequence terminating at the end of the collection with at
408412
/// most `maxLength` elements.
409413
///
410-
/// - Complexity: O(*n*), where *n* is equal to `maxLength`.
414+
/// - Complexity: O(1) if the collection conforms to
415+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is equal to
416+
/// `maxLength`.
411417
@inlinable // FIXME(sil-serialize-all)
412418
public func suffix(_ maxLength: Int) -> SubSequence {
413419
_precondition(

stdlib/public/core/ClosedRange.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,24 +232,24 @@ where Bound : Strideable, Bound.Stride : SignedInteger
232232
}
233233

234234
@inlinable
235-
public func index(_ i: Index, offsetBy n: Int) -> Index {
235+
public func index(_ i: Index, offsetBy distance: Int) -> Index {
236236
switch i {
237237
case .inRange(let x):
238238
let d = x.distance(to: upperBound)
239-
if n <= d {
240-
let newPosition = x.advanced(by: numericCast(n))
239+
if distance <= d {
240+
let newPosition = x.advanced(by: numericCast(distance))
241241
_precondition(newPosition >= lowerBound,
242242
"Advancing past start index")
243243
return .inRange(newPosition)
244244
}
245-
if d - -1 == n { return .pastEnd }
245+
if d - -1 == distance { return .pastEnd }
246246
_preconditionFailure("Advancing past end index")
247247
case .pastEnd:
248-
if n == 0 {
248+
if distance == 0 {
249249
return i
250250
}
251-
if n < 0 {
252-
return index(.inRange(upperBound), offsetBy: numericCast(n + 1))
251+
if distance < 0 {
252+
return index(.inRange(upperBound), offsetBy: numericCast(distance + 1))
253253
}
254254
_preconditionFailure("Advancing past end index")
255255
}

0 commit comments

Comments
 (0)