Skip to content

Commit

Permalink
Update guide/docs/tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
natecook1000 committed Nov 14, 2023
1 parent 2e71e22 commit 5c54f20
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 36 deletions.
79 changes: 50 additions & 29 deletions Guides/Trim.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Trim.swift) |
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/TrimTests.swift)]

Returns a `SubSequence` formed by discarding all elements at the start and end
of the collection which satisfy the given predicate.
A group of methods that return the `SubSequence` formed by discarding elements
at the start and/or end of the collection which satisfy the given predicate.

This example uses `trimming(while:)` to get a substring without the white space
at the beginning and end of the string.
Expand All @@ -17,24 +17,44 @@ let results = [2, 10, 11, 15, 20, 21, 100].trimming(while: { $0.isMultiple(of: 2
print(results) // [11, 15, 20, 21]
```

The `Algorithms` library also includes methods that trim from each end,
as well as mutating versions of all three methods.

## Detailed Design

A new method is added to `BidirectionalCollection`:
New methods are added to `Collection` and `BidirectionalCollection`:

```swift
extension Collection {
func trimmingPrefix(while predicate: (Element) throws -> Bool) rethrows -> SubSequence
}

extension BidirectionalCollection {
public func trimming(while predicate: (Element) throws -> Bool) rethrows -> SubSequence
func trimmingSuffix(while predicate: (Element) throws -> Bool) rethrows -> SubSequence

func trimming(while predicate: (Element) throws -> Bool) rethrows -> SubSequence
}

extension Collection where Self: RangeReplaceableCollection {
mutating func trimPrefix(while predicate: (Element) throws -> Bool) rethrows
}

extension BidirectionalCollection where Self: RangeReplaceableCollection {
mutating func trimSuffix(while predicate: (Element) throws -> Bool) rethrows

mutating func trim(while predicate: (Element) throws -> Bool) rethrows
}
```

This method requires `BidirectionalCollection` for an efficient implementation
which visits as few elements as possible.
There are also overloads of the mutating methods when `Self == Self.SubSequence`,
for non-range-replaceable self-slicing types.

A less-efficient implementation is _possible_ for any `Collection`, which would
involve always traversing the entire collection. This implementation is not
provided, as it would mean developers of generic algorithms who forget to add
the `BidirectionalCollection` constraint will receive that inefficient
implementation:
Though the `trimming` and `trimmingSuffix` methods are declared on
`BidirectionalCollection`, a less-efficient implementation is _possible_ for
any `Collection`, which would involve always traversing the entire collection.
This implementation is not provided, as it would mean developers of generic
algorithms who forget to add the `BidirectionalCollection` constraint will
receive that inefficient implementation:

```swift
func myAlgorithm<Input>(input: Input) where Input: Collection {
Expand All @@ -50,28 +70,29 @@ Swift provides the `BidirectionalCollection` protocol for marking types which
support reverse traversal, and generic types and algorithms which want to make
use of that should add it to their constraints.

### >= 0.3.0
#### Supporting Methods

In `v0.3.0` new methods are added to allow discarding all the elements matching
the predicate at the beginning (prefix) or at the ending (suffix) of the
collection.
- `trimmingSuffix(while:)` can only be run on collections conforming to the
`BidirectionalCollection` protocol.
- `trimmingPrefix(while:)` can be run also on collections conforming to the
`Collection` protocol.
The `endOfPrefix(while:)` and `startOfSuffix(while:)` methods are used
in the implementation of the trimming methods described above. As these
supporting methods are independently useful, they are included in the library
as well.

```swift
let myString = " hello, world "
print(myString.trimmingPrefix(while: \.isWhitespace)) // "hello, world "
extension Collection {
/// Returns the exclusive upper bound of the prefix of elements that satisfy
/// the predicate.
func endOfPrefix(
while predicate: (Element) throws -> Bool
) rethrows -> Index
}

print(myString.trimmingSuffix(while: \.isWhitespace)) // " hello, world"
```
Also mutating variants for all the methods already existing and the new ones are
added.
```swift
var myString = " hello, world "
myString.trim(while: \.isWhitespace)
print(myString) // "hello, world"
extension BidirectionalCollection {
/// Returns the inclusive lower bound of the suffix of elements that satisfy
/// the predicate.
func startOfSuffix(
while predicate: (Element) throws -> Bool
) rethrows -> Index
}
```

### Complexity
Expand Down
5 changes: 5 additions & 0 deletions Sources/Algorithms/Documentation.docc/Trimming.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ Remove unwanted elements from the start, the end, or both ends of a collection.
### Finding the Suffix of a Collection

- ``Swift/BidirectionalCollection/suffix(while:)``

### Finding Boundaries within a Collection

- ``Swift/Collection/endOfPrefix(while:)``
- ``Swift/BidirectionalCollection/startOfSuffix(while:)``
12 changes: 6 additions & 6 deletions Sources/Algorithms/Trim.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension Collection {
while predicate: (Element) throws -> Bool
) rethrows -> SubSequence {
let start = try endOfPrefix(while: predicate)
return self[start...]
return self[start..<endIndex]
}
}

Expand Down Expand Up @@ -61,8 +61,8 @@ extension Collection where Self: RangeReplaceableCollection {
public mutating func trimPrefix(
while predicate: (Element) throws -> Bool
) rethrows {
let end = try endOfPrefix(while: predicate)
removeSubrange(startIndex..<end)
let startOfResult = try endOfPrefix(while: predicate)
removeSubrange(startIndex..<startOfResult)
}
}

Expand Down Expand Up @@ -135,7 +135,7 @@ extension BidirectionalCollection {
while predicate: (Element) throws -> Bool
) rethrows -> SubSequence {
let end = try startOfSuffix(while: predicate)
return self[..<end]
return self[startIndex..<end]
}
}

Expand Down Expand Up @@ -188,8 +188,8 @@ extension BidirectionalCollection where Self: RangeReplaceableCollection {
public mutating func trimSuffix(
while predicate: (Element) throws -> Bool
) rethrows {
let start = try startOfSuffix(while: predicate)
removeSubrange(start..<endIndex)
let endOfResult = try startOfSuffix(while: predicate)
removeSubrange(endOfResult..<endIndex)
}
}

Expand Down
2 changes: 1 addition & 1 deletion Tests/SwiftAlgorithmsTests/SuffixTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//===----------------------------------------------------------------------===//

import XCTest
@testable import Algorithms
import Algorithms

final class SuffixTests: XCTestCase {
func testSuffix() {
Expand Down

0 comments on commit 5c54f20

Please sign in to comment.