Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lazy split implementation. #78

Merged
merged 36 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8c32f20
Add a lazy implementation of `split`.
toddthomas Feb 17, 2021
9640fff
Shuffle the conformance declarations and test lazy sequence conformance.
toddthomas Feb 17, 2021
90a928b
Add precondition check for `maxSplits`.
toddthomas Feb 17, 2021
84bf789
Hoist a check out of a loop and note future improvement.
toddthomas Feb 17, 2021
275eaaf
Match project style for array literals.
toddthomas Feb 17, 2021
900ae90
Test trailing multiple adjacent separators.
toddthomas Feb 17, 2021
1657e26
Simpler and more correct iteration algorithm.
toddthomas Feb 19, 2021
ae3ab9a
Implement some protocol conformance suggestions.
toddthomas Feb 19, 2021
68cd8ad
Add a test for `maxSplits == 0`.
toddthomas Feb 19, 2021
31d26f7
Fix doc typo.
toddthomas Feb 19, 2021
ab8c71d
Wrap a collection instead of a lazy collection.
toddthomas Feb 20, 2021
39f9128
Enhanced style guide conformance via swift-format.
toddthomas Feb 21, 2021
2819c44
Add `LazySplitSequence`.
toddthomas Feb 21, 2021
650d9ba
Fix lazy sequence bug with separator count >= element count.
toddthomas Feb 23, 2021
6fb80ed
Remove unnecessary constraint.
toddthomas Feb 23, 2021
3fb8bfa
Oops! Make these new public methods actually public!
toddthomas Feb 23, 2021
db48bc9
Make `LazySplitCollection` a lazy collection.
toddthomas Feb 25, 2021
d0abbe8
Remove obsolete `defer` block.
toddthomas Feb 26, 2021
fe115ed
Reorganize the lazy collection tests.
toddthomas Feb 26, 2021
db64440
Change the definition of `endIndex`.
toddthomas Feb 26, 2021
c880918
Remove risky default argument values.
toddthomas Feb 28, 2021
d30e7ae
Give `separatorCount` the more accurate name `splitCount`.
toddthomas Mar 1, 2021
a82d266
Reformat comments.
toddthomas Mar 1, 2021
34573d4
Comment `next` and simplify its termination logic.
toddthomas Mar 1, 2021
73237e9
A few more comments.
toddthomas Mar 1, 2021
0f5d9e3
swift-format gives its opinions.
toddthomas Mar 1, 2021
305a687
Use one file each for wrapper type definitions and tests.
toddthomas Mar 1, 2021
ad30d89
Update docs.
toddthomas Mar 2, 2021
4df60a6
Use sequence examples in the sequence docs.
toddthomas Mar 2, 2021
3e50f85
Better example of splitting a sequence.
toddthomas Mar 2, 2021
c524411
Add inlinablity.
toddthomas Mar 2, 2021
8c60c56
Remove unnecessary property.
toddthomas Mar 6, 2021
c205893
Fix bug when sequence starts with multiple separators.
toddthomas Mar 6, 2021
d1846fa
Use documented convention for `Hashable` conformance.
toddthomas Mar 6, 2021
7a4a0eb
Add cases with 3 separators.
toddthomas Mar 7, 2021
2001237
A few more tests to complete length 4.
toddthomas Mar 8, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions Guides/LazySplit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# LazySplit

[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/LazySplit.swift) |
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/LazySplitTests.swift)]

Lazily-evaluating versions of
[`split(maxSplits:omittingEmptySubsequences:whereSeparator:)`](https://developer.apple.com/documentation/swift/sequence/3128814-split)
and [`split(separator:maxSplits:omittingEmptySubsequences:)`](https://developer.apple.com/documentation/swift/sequence/3128818-split),
performing the same operation as their counterparts defined on
`Sequence` and `Collection`, are added to `LazySequence` and
`LazyCollection`. The `LazyCollection` methods allow splitting a
collection without allocating additional storage on the heap.

```swift
// Splitting a lazy sequence.
let numbers = stride(from: 1, through: 16, by: 1)
for subsequence in numbers.lazy.split(
whereSeparator: { $0 % 3 == 0 || $0 % 5 == 0 }
) {
print(subsequence)
}
/* Prints:
[1, 2]
[4]
[7, 8]
[11]
[13, 14]
[16]
*/

// Splitting a lazy collection.
let line = "BLANCHE: I don't want realism. I want magic!"
for subsequence in line.lazy.split(separator: " ") {
print(subsequence)
}
/* Prints
BLANCHE:
I
don't
want
realism.
I
want
magic!
*/
```

## Detailed Design

`LazySequence` and `LazyCollection` are each extended with
`split(maxSplits:omittingEmptySubsequences:whereSeparator:)` and
`split(separator:maxSplits:omittingEmptySubsequences:)`.

The `LazySequence` versions of those methods return an instance of
`LazySplitSequence`. The `LazyCollection` versions return an instance of
`LazySplitCollection`.

`LazySplitSequence` wraps the sequence to be split, and provides an
iterator whose `next` method returns a newly-allocated array containing
the elements of each subsequence in the split sequence in turn.

`LazySplitCollection` wraps the collection to be split. Its `Index`
wraps a range of base collection indices. `startIndex` is computed at
initialization. Subscripting a `LazySplitCollection` instance returns
the slice of the original collection which is the subsequence of the
split collection at the given index's position.

### Complexity

Iterating a `LazySplitSequence` instance is O(_n_) in time and space,
since each subsequence returned is a newly-allocated array.

Iterating a `LazySplitCollection` instance is O(_n_) in time and O(1) in
space, since each subsequence returned is a slice of the base
collection. Since `startIndex` is computed at initialization, some or
all of the time cost may be paid at initialization. For example, if the
base collection contains no elements determined to be separators, it
will be iterated entirely on initialization of the split collection, and
all subsequent operations on the split collection, such as
`index(after:)`, will have complexity O(1).
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Read more about the package, and the intent behind it, in the [announcement on s
- [`trimming(while:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Trim.md): Returns a slice by trimming elements from a collection's start and end.
- [`windows(ofCount:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/SlidingWindows.md): Breaks a collection into overlapping subsequences where elements are slices from the original collection.
- [`striding(by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Stride.md): Returns every nth element of a collection.
- [`split(maxSplits:omittingEmptySubsequences:whereSeparator)`, `split(separator:maxSplits:omittingEmptySubsequences)`](https://github.com/apple/swift-algorithms/blob/main/Guides/LazySplit.md): Lazy versions of the Standard Library's eager operations that split sequences and collections into subsequences separated by the specified separator element.

## Adding Swift Algorithms as a Dependency

Expand Down
Loading