Skip to content

Commit 7d03ddf

Browse files
committed
[stdlib] Update complexity docs for seq/collection algorithms (swiftlang#17254)
* [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 cb01e10 commit 7d03ddf

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
@@ -125,6 +125,8 @@ where SubSequence: BidirectionalCollection, Indices: BidirectionalCollection {
125125
///
126126
/// - Parameter bounds: A range of the collection's indices. The bounds of
127127
/// the range must be valid indices of the collection.
128+
///
129+
/// - Complexity: O(1)
128130
subscript(bounds: Range<Index>) -> SubSequence { get }
129131

130132
// FIXME(ABI): Associated type inference requires this.
@@ -147,26 +149,26 @@ extension BidirectionalCollection {
147149
}
148150

149151
@inlinable // FIXME(sil-serialize-all)
150-
public func index(_ i: Index, offsetBy n: Int) -> Index {
151-
if n >= 0 {
152-
return _advanceForward(i, by: n)
152+
public func index(_ i: Index, offsetBy distance: Int) -> Index {
153+
if distance >= 0 {
154+
return _advanceForward(i, by: distance)
153155
}
154156
var i = i
155-
for _ in stride(from: 0, to: n, by: -1) {
157+
for _ in stride(from: 0, to: distance, by: -1) {
156158
formIndex(before: &i)
157159
}
158160
return i
159161
}
160162

161163
@inlinable // FIXME(sil-serialize-all)
162164
public func index(
163-
_ i: Index, offsetBy n: Int, limitedBy limit: Index
165+
_ i: Index, offsetBy distance: Int, limitedBy limit: Index
164166
) -> Index? {
165-
if n >= 0 {
166-
return _advanceForward(i, by: n, limitedBy: limit)
167+
if distance >= 0 {
168+
return _advanceForward(i, by: distance, limitedBy: limit)
167169
}
168170
var i = i
169-
for _ in stride(from: 0, to: n, by: -1) {
171+
for _ in stride(from: 0, to: distance, by: -1) {
170172
if i == limit {
171173
return nil
172174
}
@@ -207,7 +209,7 @@ extension BidirectionalCollection where SubSequence == Self {
207209
/// - Returns: The last element of the collection if the collection has one
208210
/// or more elements; otherwise, `nil`.
209211
///
210-
/// - Complexity: O(1).
212+
/// - Complexity: O(1)
211213
@inlinable // FIXME(sil-serialize-all)
212214
public mutating func popLast() -> Element? {
213215
guard !isEmpty else { return nil }
@@ -234,20 +236,20 @@ extension BidirectionalCollection where SubSequence == Self {
234236

235237
/// Removes the given number of elements from the end of the collection.
236238
///
237-
/// - Parameter n: The number of elements to remove. `n` must be greater
239+
/// - Parameter k: The number of elements to remove. `k` must be greater
238240
/// than or equal to zero, and must be less than or equal to the number of
239241
/// elements in the collection.
240242
///
241243
/// - Complexity: O(1) if the collection conforms to
242-
/// `RandomAccessCollection`; otherwise, O(*n*), where *n* is the length
243-
/// of the collection.
244+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the number of
245+
/// elements to remove.
244246
@inlinable // FIXME(sil-serialize-all)
245-
public mutating func removeLast(_ n: Int) {
246-
if n == 0 { return }
247-
_precondition(n >= 0, "Number of elements to remove should be non-negative")
248-
_precondition(count >= n,
247+
public mutating func removeLast(_ k: Int) {
248+
if k == 0 { return }
249+
_precondition(k >= 0, "Number of elements to remove should be non-negative")
250+
_precondition(count >= k,
249251
"Can't remove more items from a collection than it contains")
250-
self = self[startIndex..<index(endIndex, offsetBy: -n)]
252+
self = self[startIndex..<index(endIndex, offsetBy: -k)]
251253
}
252254
}
253255

@@ -264,18 +266,20 @@ extension BidirectionalCollection {
264266
/// print(numbers.dropLast(10))
265267
/// // Prints "[]"
266268
///
267-
/// - Parameter n: The number of elements to drop off the end of the
268-
/// collection. `n` must be greater than or equal to zero.
269-
/// - Returns: A subsequence that leaves off `n` elements from the end.
269+
/// - Parameter k: The number of elements to drop off the end of the
270+
/// collection. `k` must be greater than or equal to zero.
271+
/// - Returns: A subsequence that leaves off `k` elements from the end.
270272
///
271-
/// - Complexity: O(*n*), where *n* is the number of elements to drop.
273+
/// - Complexity: O(1) if the collection conforms to
274+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the number of
275+
/// elements to drop.
272276
@inlinable // FIXME(sil-serialize-all)
273-
public func dropLast(_ n: Int) -> SubSequence {
277+
public func dropLast(_ k: Int) -> SubSequence {
274278
_precondition(
275-
n >= 0, "Can't drop a negative number of elements from a collection")
279+
k >= 0, "Can't drop a negative number of elements from a collection")
276280
let end = index(
277281
endIndex,
278-
offsetBy: -n,
282+
offsetBy: -k,
279283
limitedBy: startIndex) ?? startIndex
280284
return self[startIndex..<end]
281285
}
@@ -297,7 +301,9 @@ extension BidirectionalCollection {
297301
/// - Returns: A subsequence terminating at the end of the collection with at
298302
/// most `maxLength` elements.
299303
///
300-
/// - Complexity: O(*n*), where *n* is equal to `maxLength`.
304+
/// - Complexity: O(1) if the collection conforms to
305+
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is equal to
306+
/// `maxLength`.
301307
@inlinable // FIXME(sil-serialize-all)
302308
public func suffix(_ maxLength: Int) -> SubSequence {
303309
_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)