Skip to content

Commit c5ea9e3

Browse files
author
Tim Vermeulen
authored
Prepare for Swift Algorithms 1.0 (apple#167)
* Rename types to include a Sequence or Collection suffix, and clean up some guides * Various refactors * Replace `validateIndexTraversals` test method with `IndexValidator` type * Add LazySequenceProtocol to StridingSequence, StridingCollection * Update the Numerics dependency to 1.0 * Set `IndexedCollection.Indices` to `Base.Indices` * Remove deprecations * Update the changelog/README for the 1.0.0 release * Fix compiler error on Linux * Rename `WindowsCollection` to `WindowsOfCountCollection`
1 parent e195266 commit c5ea9e3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1615
-1280
lines changed

.github/ISSUE_TEMPLATE/BUG_REPORT.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ about: Something isn't working as expected
1616

1717
Replace this paragraph with a short description of the incorrect incorrect behavior. If this is a regression, please note the last version that the behavior was correct in addition to your current version.
1818

19-
**Swift Algorithms version:** `0.0.1` or the `main` branch, for example.
19+
**Swift Algorithms version:** `1.0.0` or the `main` branch, for example.
2020
**Swift version:** Paste the output of `swift --version` here.
2121

2222
### Checklist

CHANGELOG.md

+72-6
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,81 @@
44
Add new items at the end of the relevant section under **Unreleased**.
55
-->
66

7-
This project follows semantic versioning. While still in major version `0`,
8-
source-stability is only guaranteed within minor versions (e.g. between
9-
`0.0.3` and `0.0.4`). If you want to guard against potentially source-breaking
10-
package updates, you can specify your package dependency using
11-
`.upToNextMinor(from: "0.1.0")` as the requirement.
7+
This project follows semantic versioning.
128

139
## [Unreleased]
1410

1511
*No new changes.*
1612

1713
---
1814

15+
## [1.0.0] - 2021-09-08
16+
17+
### Changes
18+
19+
- Most sequence and collection types have been renamed, following a more
20+
consistent naming structure:
21+
- The `Lazy` prefix was dropped.
22+
- Either a `Sequence` or `Collection` suffix was added depending on whether or
23+
not the type is unconditionally a collection.
24+
- The base name was derived from the name of the method that produces it,
25+
including an argument label to disambiguate if necessary.
26+
```
27+
Chain2 -> Chain2Sequence
28+
ChunkedBy -> ChunkedByCollection
29+
ChunkedOn -> ChunkedOnCollection
30+
ChunkedByCount -> ChunksOfCountCollection
31+
Combinations -> CombinationsSequence
32+
Cycle -> CycledSequence
33+
FiniteCycle -> CycledTimesCollection
34+
Indexed -> IndexedCollection
35+
Intersperse -> InterspersedSequence
36+
LazySplitSequence -> SplitSequence
37+
LazySplitCollection -> SplitCollection
38+
Permutations -> PermutationsSequence
39+
UniquePermutations -> UniquePermutationsSequence
40+
Product2 -> Product2Sequence
41+
ExclusiveReductions -> ExclusiveReductionsSequence
42+
InclusiveReductions -> InclusiveReductionsSequence
43+
StrideSequence -> StridingSequence
44+
StrideCollection -> StridingCollection
45+
Uniqued -> UniquedSequence
46+
Windows -> WindowsOfCountCollection
47+
```
48+
- Types that can only be produced from a lazy sequence chain now unconditionally
49+
conform to `LazySequenceProtocol` and wrap the base sequence instead of the
50+
lazy wrapper, making some return types slightly simpler.
51+
- e.g. `[1, 2, 3].lazy.reductions(+)` now returns
52+
`ExclusiveReductionsSequence<[Int]>`, not
53+
`ExclusiveReductionsSequence<LazySequence<[Int]>>`.
54+
- This concerns `JoinedByClosureSequence`, `JoinedByClosureCollection`,
55+
`ExclusiveReductionsSequence`, `InclusiveReductionsSequence`.
56+
- The generic parameters of the `ExclusiveReductions` type have been swapped,
57+
putting the base collection first and the result type second.
58+
- The `Indices` associated type of `IndexedCollection` now matches
59+
`Base.Indices`.
60+
61+
### Removals
62+
63+
- Previously deprecated type and method names have been removed:
64+
- The `Chain` type alias for `Chain2Sequence`
65+
- The `chained(with:)` method which was replaced with the `chain(_:_:)` free
66+
function
67+
- The `LazyChunked` and `Chunked` type aliases for `ChunkedByCollection`
68+
- The `rotate(subrange:at:)` and `rotate(at:)` methods which were renamed to
69+
`rotate(subrange:toStartAt:)` and `rotate(toStartAt:)` respectively
70+
71+
### Fixes
72+
73+
- The `StridingSequence` and `StridingCollection` types now conditionally
74+
conform to `LazySequenceProtocol`, allowing the `striding(by:)` method to
75+
properly propagate laziness in a lazy sequence chain.
76+
- Fixed `chunked(by:)` to always compare two consecutive elements rather than
77+
each element with the first element of the current chunk. ([#162])
78+
79+
The 1.0.0 release includes contributions from [iainsmith], [mdznr], and
80+
[timvermeulen]. Thank you!
81+
1982
## [0.2.1] - 2021-06-01
2083

2184
### Additions
@@ -206,7 +269,8 @@ This changelog's format is based on [Keep a Changelog](https://keepachangelog.co
206269

207270
<!-- Link references for releases -->
208271

209-
[Unreleased]: https://github.com/apple/swift-algorithms/compare/0.2.1...HEAD
272+
[Unreleased]: https://github.com/apple/swift-algorithms/compare/1.0.0...HEAD
273+
[1.0.0]: https://github.com/apple/swift-algorithms/compare/0.2.1...1.0.0
210274
[0.2.1]: https://github.com/apple/swift-algorithms/compare/0.2.0...0.2.1
211275
[0.2.0]: https://github.com/apple/swift-algorithms/compare/0.1.1...0.2.0
212276
[0.1.1]: https://github.com/apple/swift-algorithms/compare/0.1.0...0.1.1
@@ -243,6 +307,7 @@ This changelog's format is based on [Keep a Changelog](https://keepachangelog.co
243307
[#125]: https://github.com/apple/swift-algorithms/pull/125
244308
[#130]: https://github.com/apple/swift-algorithms/pull/130
245309
[#138]: https://github.com/apple/swift-algorithms/pull/138
310+
[#162]: https://github.com/apple/swift-algorithms/pull/162
246311

247312
<!-- Link references for contributors -->
248313

@@ -256,6 +321,7 @@ This changelog's format is based on [Keep a Changelog](https://keepachangelog.co
256321
[fedeci]: https://github.com/apple/swift-algorithms/commits?author=fedeci
257322
[hashemi]: https://github.com/apple/swift-algorithms/commits?author=hashemi
258323
[IanKeen]: https://github.com/apple/swift-algorithms/commits?author=IanKeen
324+
[iainsmith]: https://github.com/apple/swift-algorithms/commits?author=iainsmith
259325
[iSame7]: https://github.com/apple/swift-algorithms/commits?author=iSame7
260326
[karwa]: https://github.com/apple/swift-algorithms/commits?author=karwa
261327
[kylemacomber]: https://github.com/apple/swift-algorithms/commits?author=kylemacomber

Guides/AdjacentPairs.md

+22-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
Lazily iterates over tuples of adjacent elements.
77

8-
This operation is available for any sequence by calling the `adjacentPairs()` method.
8+
This operation is available for any sequence by calling the `adjacentPairs()`
9+
method.
910

1011
```swift
1112
let numbers = (1...5)
@@ -15,38 +16,48 @@ let pairs = numbers.adjacentPairs()
1516

1617
## Detailed Design
1718

18-
The `adjacentPairs()` method is declared as a `Sequence` extension returning `AdjacentPairsSequence` and as a `Collection` extension returning `AdjacentPairsCollection`.
19+
The `adjacentPairs()` method is declared as a `Sequence` extension returning
20+
`AdjacentPairsSequence` and as a `Collection` extension returning
21+
`AdjacentPairsCollection`.
1922

2023
```swift
2124
extension Sequence {
22-
public func adjacentPairs() -> AdjacentPairsSequence<Self>
25+
public func adjacentPairs() -> AdjacentPairsSequence<Self>
2326
}
2427
```
2528

2629
```swift
2730
extension Collection {
28-
public func adjacentPairs() -> AdjacentPairsCollection<Self>
31+
public func adjacentPairs() -> AdjacentPairsCollection<Self>
2932
}
3033
```
3134

32-
The `AdjacentPairsSequence` type is a sequence, and the `AdjacentPairsCollection` type is a collection with conditional conformance to `BidirectionalCollection` and `RandomAccessCollection` when the underlying collection conforms.
35+
The `AdjacentPairsSequence` type is a sequence, and the
36+
`AdjacentPairsCollection` type is a collection with conditional conformance to
37+
`BidirectionalCollection` and `RandomAccessCollection` when the underlying
38+
collection conforms.
3339

3440
### Complexity
3541

3642
Calling `adjacentPairs` is an O(1) operation.
3743

3844
### Naming
3945

40-
This method is named for clarity while remaining agnostic to any particular domain of programming. In natural language processing, this operation is akin to computing a list of bigrams; however, this algorithm is not specific to this use case.
41-
42-
[naming]: https://forums.swift.org/t/naming-of-chained-with/40999/
46+
This method is named for clarity while remaining agnostic to any particular
47+
domain of programming. In natural language processing, this operation is akin to
48+
computing a list of bigrams; however, this algorithm is not specific to this use
49+
case.
4350

4451
### Comparison with other languages
4552

46-
This function is often written as a `zip` of a sequence together with itself, minus its first element.
53+
This function is often written as a `zip` of a sequence together with itself,
54+
minus its first element.
4755

4856
**Haskell:** This operation is spelled ``s `zip` tail s``.
4957

50-
**Python:** Python users may write `zip(s, s[1:])` for a list with at least one element. For natural language processing, the `nltk` package offers a `bigrams` function akin to this method.
58+
**Python:** Python users may write `zip(s, s[1:])` for a list with at least one
59+
element. For natural language processing, the `nltk` package offers a `bigrams`
60+
function akin to this method.
5161

52-
Note that in Swift, the spelling `zip(s, s.dropFirst())` is undefined behavior for a single-pass sequence `s`.
62+
Note that in Swift, the spelling `zip(s, s.dropFirst())` is undefined behavior
63+
for a single-pass sequence `s`.

Guides/Chain.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ the shared conformances of the two underlying types.
2525
The `chain(_:_:)` function takes two sequences as arguments:
2626

2727
```swift
28-
public func chain<S1, S2>(_ s1: S1, _ s2: S2) -> Chain2<S1, S2>
28+
public func chain<S1, S2>(_ s1: S1, _ s2: S2) -> Chain2Sequence<S1, S2>
2929
where S1.Element == S2.Element
3030
```
3131

32-
The resulting `Chain2` type is a sequence, with conditional conformance to
33-
`Collection`, `BidirectionalCollection`, and `RandomAccessCollection` when both
34-
the first and second arguments conform.
32+
The resulting `Chain2Sequence` type is a sequence, with conditional conformance
33+
to `Collection`, `BidirectionalCollection`, and `RandomAccessCollection` when
34+
both the first and second arguments conform.
3535

3636
### Naming
3737

Guides/Chunked.md

+53-40
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/ChunkedTests.swift)]
55

66
Break a collection into subsequences where consecutive elements pass a binary
7-
predicate, or where all elements in each chunk project to the same value.
7+
predicate, or where all elements in each chunk project to the same value.
88

9-
Also, includes a `chunks(ofCount:)` that breaks a collection into subsequences
9+
Also includes a `chunks(ofCount:)` that breaks a collection into subsequences
1010
of a given `count`.
1111

1212
There are two variations of the `chunked` method: `chunked(by:)` and
@@ -20,22 +20,22 @@ let chunks = numbers.chunked(by: { $0 <= $1 })
2020
// [[10, 20, 30], [10, 40, 40], [10, 20]]
2121
```
2222

23-
The `chunk(on:)` method, by contrast, takes a projection of each element and
23+
The `chunked(on:)` method, by contrast, takes a projection of each element and
2424
separates chunks where the projection of two consecutive elements is not equal.
25-
The result includes both the projected value and the subsequence
26-
that groups elements with that projected value:
25+
The result includes both the projected value and the subsequence that groups
26+
elements with that projected value:
2727

2828
```swift
2929
let names = ["David", "Kyle", "Karoy", "Nate"]
3030
let chunks = names.chunked(on: \.first!)
3131
// [("D", ["David"]), ("K", ["Kyle", "Karoy"]), ("N", ["Nate"])]
3232
```
3333

34-
The `chunks(ofCount:)` method takes a `count` parameter (greater than zero)
35-
and separates the collection into chunks of this given count.
36-
If the `count` parameter is evenly divided by the count of the base `Collection`,
37-
all the chunks will have a count equal to the parameter.
38-
Otherwise, the last chunk will contain the remaining elements.
34+
The `chunks(ofCount:)` method takes a `count` parameter (greater than zero)
35+
and separates the collection into chunks of this given count. If the `count`
36+
parameter is evenly divided by the count of the base `Collection`, all the
37+
chunks will have a count equal to the parameter. Otherwise, the last chunk will
38+
contain the remaining elements.
3939

4040
```swift
4141
let names = ["David", "Kyle", "Karoy", "Nate"]
@@ -46,11 +46,11 @@ let remaining = names.chunks(ofCount: 3)
4646
// equivalent to [["David", "Kyle", "Karoy"], ["Nate"]]
4747
```
4848

49-
The `chunks(ofCount:)` is the subject of an [existing SE proposal][proposal].
49+
The `chunks(ofCount:)` is the subject of an [existing SE proposal][proposal].
5050

51-
When "chunking" a collection, the entire collection is included in the result,
51+
When "chunking" a collection, the entire collection is included in the result,
5252
unlike the `split` family of methods, where separators are dropped.
53-
Joining the result of a chunking method call recreates the original collection.
53+
Joining the result of a chunking method call recreates the original collection.
5454

5555
```swift
5656
c.elementsEqual(c.chunked(...).joined())
@@ -61,46 +61,59 @@ c.elementsEqual(c.chunked(...).joined())
6161

6262
## Detailed Design
6363

64-
The two methods are added as extension to `Collection`, with two matching
65-
versions that return a lazy wrapper added to `LazyCollectionProtocol`.
64+
The three methods are added as extension to `Collection`. `chunked(by:)` and
65+
`chunked(on:)` are eager by default, both with a matching version that return a
66+
lazy wrapper added to `LazySequenceProtocol`.
6667

6768
```swift
6869
extension Collection {
69-
public func chunked(
70-
by belongInSameGroup: (Element, Element) -> Bool
71-
) -> [SubSequence]
72-
73-
public func chunked<Subject: Equatable>(
74-
on projection: (Element) -> Subject
75-
) -> [(Subject, SubSequence)]
76-
}
77-
78-
extension LazyCollectionProtocol {
79-
public func chunked(
80-
by belongInSameGroup: @escaping (Element, Element) -> Bool
81-
) -> ChunkedBy<Elements, Element>
82-
83-
public func chunked<Subject: Equatable>(
84-
on projection: @escaping (Element) -> Subject
85-
) -> ChunkedOn<Elements, Subject>
70+
public func chunked(
71+
by belongInSameGroup: (Element, Element) -> Bool
72+
) -> [SubSequence]
73+
74+
public func chunked<Subject: Equatable>(
75+
on projection: (Element) -> Subject
76+
) -> [(Subject, SubSequence)]
77+
78+
public func chunks(ofCount count: Int) -> ChunksOfCountCollection<Self>
79+
}
80+
81+
extension LazySequenceProtocol where Self: Collection, Elements: Collection {
82+
public func chunked(
83+
by belongInSameGroup: @escaping (Element, Element) -> Bool
84+
) -> ChunkedByCollection<Elements, Element>
85+
86+
public func chunked<Subject: Equatable>(
87+
on projection: @escaping (Element) -> Subject
88+
) -> ChunkedOnCollection<Elements, Subject>
8689
}
8790
```
8891

89-
The `ChunkedBy` and `ChunkedOn` types are bidirectional when the wrapped collection is
90-
bidirectional.
92+
The `ChunkedByCollection`, `ChunkedOnCollection`, and `ChunksOfCountCollection`
93+
types are bidirectional when the wrapped collection is bidirectional.
94+
`ChunksOfCountCollection` also conforms to `LazySequenceProtocol` when the base
95+
collection conforms.
9196

9297
### Complexity
9398

9499
The eager methods are O(_n_), the lazy methods are O(_1_).
95100

96101
### Naming
97102

98-
The operation performed by these methods is similar to other ways of breaking a collection up into subsequences. In particular, the predicate-based `split(where:)` method looks similar to `chunked(on:)`. You can draw a distinction between these different operations based on the resulting subsequences:
99-
100-
- `split`: *In the standard library.* Breaks a collection into subsequences, removing any elements that are considered "separators". The original collection cannot be recovered from the result of splitting.
101-
- `chunked`: *In this package.* Breaks a collection into subsequences, preserving each element in its initial ordering. Joining the resulting subsequences re-forms the original collection.
102-
- `sliced`: *Not included in this package or the stdlib.* Breaks a collection into potentially overlapping subsequences.
103-
103+
The operation performed by these methods is similar to other ways of breaking a
104+
collection up into subsequences. In particular, the predicate-based
105+
`split(where:)` method looks similar to `chunked(on:)`. You can draw a
106+
distinction between these different operations based on the resulting
107+
subsequences:
108+
109+
- `split`: *In the standard library.* Breaks a collection into subsequences,
110+
removing any elements that are considered "separators". The original collection
111+
cannot be recovered from the result of splitting.
112+
- `chunked`: *In this package.* Breaks a collection into subsequences,
113+
preserving each element in its initial ordering. Joining the resulting
114+
subsequences re-forms the original collection.
115+
- `sliced`: *Not included in this package or the stdlib.* Breaks a collection
116+
into potentially overlapping subsequences.
104117

105118
### Comparison with other languages
106119

Guides/Combinations.md

+11-10
Original file line numberDiff line numberDiff line change
@@ -60,27 +60,28 @@ for combo in numbers.combinations(ofCount: 2...3) {
6060
## Detailed Design
6161

6262
The `combinations(ofCount:)` method is declared as a `Collection` extension,
63-
and returns a `Combinations` type:
63+
and returns a `CombinationsSequence` type:
6464

6565
```swift
6666
extension Collection {
67-
public func combinations(ofCount k: Int) -> Combinations<Self>
67+
public func combinations(ofCount k: Int) -> CombinationsSequence<Self>
6868
}
6969
```
7070

71-
Since the `Combinations` type needs to store an array of the collection’s
72-
indices and mutate the array to generate each permutation, `Combinations` only
73-
has `Sequence` conformance. Adding `Collection` conformance would require
74-
storing the array in the index type, which would in turn lead to copying the
75-
array at every index advancement. `Combinations` does conform to
76-
`LazySequenceProtocol` when the base type conforms.
71+
Since the `CombinationsSequence` type needs to store an array of the
72+
collection’s indices and mutate the array to generate each permutation,
73+
`CombinationsSequence` only has `Sequence` conformance. Adding `Collection`
74+
conformance would require storing the array in the index type, which would in
75+
turn lead to copying the array at every index advancement.
76+
`CombinationsSequence` does conform to `LazySequenceProtocol` when the base type
77+
conforms.
7778

7879
### Complexity
7980

8081
Calling `combinations(ofCount:)` accesses the count of the collection, so it’s
8182
an O(1) operation for random-access collections, or an O(_n_) operation
82-
otherwise. Creating the iterator for a `Combinations` instance and each call to
83-
`Combinations.Iterator.next()` is an O(_n_) operation.
83+
otherwise. Creating the iterator for a `CombinationsSequence` instance and each
84+
call to `CombinationsSequence.Iterator.next()` is an O(_n_) operation.
8485

8586
### Naming
8687

0 commit comments

Comments
 (0)