Skip to content

Commit c9fedd7

Browse files
author
Tim Vermeulen
authored
Restore "More algorithms" (swiftlang#33)
* Restore "More algorithms" * Fix CI
1 parent e1a8e67 commit c9fedd7

22 files changed

+863
-396
lines changed

Sources/Algorithms/Algorithms/Contains.swift

+14
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,17 @@ extension BidirectionalCollection where Element: Comparable {
99
firstRange(of: other) != nil
1010
}
1111
}
12+
13+
extension Collection {
14+
public func contains<Searcher: CollectionSearcher>(_ searcher: Searcher) -> Bool where Searcher.Searched == SubSequence {
15+
firstRange(of: searcher) != nil
16+
}
17+
}
18+
19+
// MARK: Regex
20+
21+
extension Collection where SubSequence == Substring {
22+
public func contains(_ regex: Regex) -> Bool {
23+
contains(RegexConsumer(regex: regex))
24+
}
25+
}
+26-11
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,44 @@
11
extension Collection {
2-
public func firstRange<S: CollectionSearcher>(_ searcher: S) -> Range<Index>? where S.Searched == Self {
3-
var state = searcher.initialState(self)
4-
return searcher.search(self, &state)
2+
public func firstRange<S: CollectionSearcher>(of searcher: S) -> Range<Index>? where S.Searched == SubSequence {
3+
var state = searcher.state(startingAt: startIndex, in: self[...])
4+
return searcher.search(self[...], &state)
55
}
66
}
77

88
extension BidirectionalCollection {
9-
public func lastRange<S: BackwardCollectionSearcher>(_ searcher: S) -> Range<Index>? where S.Searched == Self {
10-
var state = searcher.initialState(self)
11-
return searcher.searchBack(self, &state)
9+
public func lastRange<S: BackwardCollectionSearcher>(of searcher: S) -> Range<Index>? where S.Searched == SubSequence {
10+
var state = searcher.backwardState(startingAt: endIndex, in: self[...])
11+
return searcher.searchBack(self[...], &state)
1212
}
1313
}
1414

1515
extension Collection where Element: Equatable {
1616
public func firstRange<S: Sequence>(of sequence: S) -> Range<Index>? where S.Element == Element {
1717
// TODO: Use a more efficient search algorithm
18-
let searcher = ZSearcher<Self>(pattern: Array(sequence), by: ==)
19-
return searcher.search(self)
18+
let searcher = ZSearcher<SubSequence>(pattern: Array(sequence), by: ==)
19+
return searcher.search(self[...], from: startIndex)
2020
}
2121
}
2222

2323
extension BidirectionalCollection where Element: Comparable {
2424
public func firstRange<S: Sequence>(of other: S) -> Range<Index>? where S.Element == Element {
25-
let searcher = TwoWaySearcher<Self>(pattern: Array(other))
26-
var state = searcher.initialState(self)
27-
return searcher.search(self, &state)
25+
let searcher = PatternOrEmpty(searcher: TwoWaySearcher<SubSequence>(pattern: Array(other)))
26+
var state = searcher.state(startingAt: startIndex, in: self[...])
27+
return searcher.search(self[...], &state)
2828
}
2929
}
30+
31+
// MARK: Regex
32+
33+
extension Collection where SubSequence == Substring {
34+
public func firstRange(of regex: Regex) -> Range<Index>? {
35+
firstRange(of: RegexConsumer(regex: regex))
36+
}
37+
}
38+
39+
extension BidirectionalCollection where SubSequence == Substring {
40+
public func lastRange(of regex: Regex) -> Range<Index>? {
41+
lastRange(of: RegexConsumer(regex: regex))
42+
}
43+
}
44+
+98-71
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,130 @@
1-
extension Collection {
2-
public func ranges<S: CollectionSearcher>(_ searcher: S) -> RangesSequence<Self, S> {
3-
RangesSequence(base: self, searcher: searcher)
4-
}
5-
}
1+
public struct RangesCollection<Searcher: CollectionSearcher> {
2+
public typealias Base = Searcher.Searched
3+
4+
let base: Base
5+
let searcher: Searcher
6+
private(set) public var startIndex: Index
67

7-
extension Collection where Element: Equatable {
8-
public func ranges<S: Sequence>(
9-
of other: S
10-
) -> RangesSequence<Self, ZSearcher<Self>> where S.Element == Element {
11-
ranges(ZSearcher(pattern: Array(other), by: ==))
12-
}
13-
}
8+
init(base: Base, searcher: Searcher) {
9+
self.base = base
10+
self.searcher = searcher
11+
12+
let startIndex = base.startIndex
13+
var state = searcher.state(startingAt: startIndex, in: base[...])
14+
self.startIndex = Index(range: nil, state: state)
1415

15-
extension BidirectionalCollection where Element: Comparable {
16-
public func ranges<S: Sequence>(
17-
of other: S
18-
) -> RangesSequence<Self, TwoWaySearcher<Self>> where S.Element == Element {
19-
ranges(TwoWaySearcher(pattern: Array(other)))
16+
if let range = searcher.search(base[...], &state) {
17+
self.startIndex = Index(range: range, state: state)
18+
} else {
19+
self.startIndex = endIndex
20+
}
2021
}
2122
}
2223

23-
public struct RangesSequence<Base, Searcher: CollectionSearcher>
24-
where Searcher.Searched == Base
25-
{
26-
let base: Base
27-
let searcher: Searcher
28-
}
29-
30-
extension RangesSequence: Sequence {
24+
extension RangesCollection: Sequence {
3125
public struct Iterator: IteratorProtocol {
3226
let base: Base
33-
var searcher: Searcher
34-
var searcherState: Searcher.State
35-
var index: Base.Index
36-
27+
let searcher: Searcher
28+
var state: Searcher.State
29+
30+
init(base: Base, searcher: Searcher) {
31+
self.base = base
32+
self.searcher = searcher
33+
self.state = searcher.state(startingAt: base.startIndex, in: base)
34+
}
35+
3736
public mutating func next() -> Range<Base.Index>? {
38-
guard let range = searcher.search(base, &searcherState) else { return nil }
39-
index = range.upperBound
40-
return range
37+
searcher.search(base, &state)
4138
}
4239
}
43-
40+
4441
public func makeIterator() -> Iterator {
45-
Iterator(
46-
base: base,
47-
searcher: searcher,
48-
searcherState: searcher.initialState(base),
49-
index: base.startIndex)
42+
Iterator(base: base, searcher: searcher)
5043
}
5144
}
5245

53-
// TODO: consider making this a collection even when the searcher maintains a state,
54-
// and storing this state inside the `Index`.
55-
extension RangesSequence: Collection where Searcher: StatelessCollectionSearcher {
46+
extension RangesCollection: Collection {
5647
public struct Index {
57-
let range: Range<Searcher.Searched.Index>
48+
var range: Range<Searcher.Searched.Index>?
49+
var state: Searcher.State
5850
}
59-
60-
public var startIndex: Index {
61-
_index(after: base.startIndex)
62-
}
63-
51+
6452
public var endIndex: Index {
65-
Index(range: base.endIndex..<base.endIndex)
53+
// TODO: Avoid calling `state(startingAt:in:)` here
54+
Index(
55+
range: nil,
56+
state: searcher.state(startingAt: base.endIndex, in: base[...]))
6657
}
67-
68-
func _index(after index: Base.Index) -> Index {
69-
if let range = searcher.search(base, subrange: index..<base.endIndex) {
70-
return Index(range: range)
71-
} else {
72-
return endIndex
73-
}
58+
59+
public func formIndex(after index: inout Index) {
60+
guard index != endIndex else { fatalError("Cannot advance past endIndex") }
61+
index.range = searcher.search(base[...], &index.state)
7462
}
75-
63+
7664
public func index(after index: Index) -> Index {
77-
_index(after: index.range.upperBound)
65+
var index = index
66+
formIndex(after: &index)
67+
return index
7868
}
79-
69+
8070
public subscript(index: Index) -> Range<Base.Index> {
81-
precondition(index != endIndex)
82-
return index.range
71+
guard let range = index.range else { fatalError("Cannot subscript using endIndex") }
72+
return range
8373
}
8474
}
85-
86-
extension RangesSequence.Index: Comparable {
75+
76+
extension RangesCollection.Index: Comparable {
8777
public static func == (lhs: Self, rhs: Self) -> Bool {
88-
lhs.range.lowerBound == rhs.range.lowerBound
78+
switch (lhs.range, rhs.range) {
79+
case (nil, nil):
80+
return true
81+
case (nil, _?), (_?, nil):
82+
return false
83+
case (let lhs?, let rhs?):
84+
return lhs.lowerBound == rhs.lowerBound
85+
}
8986
}
90-
87+
9188
public static func < (lhs: Self, rhs: Self) -> Bool {
92-
lhs.range.lowerBound < rhs.range.lowerBound
89+
switch (lhs.range, rhs.range) {
90+
case (nil, _):
91+
return false
92+
case (_, nil):
93+
return true
94+
case (let lhs?, let rhs?):
95+
return lhs.lowerBound < rhs.lowerBound
96+
}
9397
}
9498
}
9599

96-
extension RangesSequence: BidirectionalCollection
97-
where Searcher: StatelessBidirectionalCollectionSearcher
98-
{
99-
public func index(before index: Index) -> Index {
100-
let range = searcher.searchBack(base, subrange: base.startIndex..<index.range.lowerBound)!
101-
return Index(range: range)
100+
// TODO: `BidirectionalCollection` conformance
101+
102+
extension Collection {
103+
public func ranges<S: CollectionSearcher>(of searcher: S) -> RangesCollection<S> where S.Searched == SubSequence {
104+
RangesCollection(base: self[...], searcher: searcher)
105+
}
106+
}
107+
108+
extension Collection where Element: Equatable {
109+
public func ranges<S: Sequence>(
110+
of other: S
111+
) -> RangesCollection<ZSearcher<SubSequence>> where S.Element == Element {
112+
ranges(of: ZSearcher(pattern: Array(other), by: ==))
113+
}
114+
}
115+
116+
extension BidirectionalCollection where Element: Comparable {
117+
public func ranges<S: Sequence>(
118+
of other: S
119+
) -> RangesCollection<PatternOrEmpty<TwoWaySearcher<SubSequence>>> where S.Element == Element {
120+
ranges(of: PatternOrEmpty(searcher: TwoWaySearcher(pattern: Array(other))))
121+
}
122+
}
123+
124+
// MARK: Regex
125+
126+
extension BidirectionalCollection where SubSequence == Substring {
127+
public func ranges(of regex: Regex) -> RangesCollection<RegexConsumer> {
128+
ranges(of: RegexConsumer(regex: regex))
102129
}
103130
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
extension RangeReplaceableCollection {
2+
public func replacing<Searcher: CollectionSearcher, Replacement: Collection>(
3+
_ searcher: Searcher,
4+
with replacement: Replacement,
5+
subrange: Range<Index>,
6+
maxReplacements: Int = .max
7+
) -> Self where Searcher.Searched == SubSequence, Replacement.Element == Element {
8+
// TODO: `Searcher.Searched == Self`?
9+
10+
precondition(maxReplacements >= 0)
11+
12+
var index = subrange.lowerBound
13+
var result = Self()
14+
result.append(contentsOf: self[..<index])
15+
16+
for range in self[subrange].ranges(of: searcher).prefix(maxReplacements) {
17+
result.append(contentsOf: self[index..<range.lowerBound])
18+
result.append(contentsOf: replacement)
19+
index = range.upperBound
20+
}
21+
22+
result.append(contentsOf: self[index...])
23+
return result
24+
}
25+
26+
public func replacing<Searcher: CollectionSearcher, Replacement: Collection>(
27+
_ searcher: Searcher,
28+
with replacement: Replacement,
29+
maxReplacements: Int = .max
30+
) -> Self where Searcher.Searched == SubSequence, Replacement.Element == Element {
31+
replacing(
32+
searcher,
33+
with: replacement,
34+
subrange: startIndex..<endIndex,
35+
maxReplacements: maxReplacements)
36+
}
37+
38+
public mutating func replace<Searcher: CollectionSearcher, Replacement: Collection>(
39+
_ searcher: Searcher,
40+
with replacement: Replacement,
41+
maxReplacements: Int = .max
42+
) where Searcher.Searched == SubSequence, Replacement.Element == Element {
43+
self = replacing(
44+
searcher,
45+
with: replacement,
46+
maxReplacements: maxReplacements)
47+
}
48+
}
49+
50+
// MARK: Regex
51+
52+
extension RangeReplaceableCollection where SubSequence == Substring {
53+
public func replacing<Replacement: Collection>(
54+
_ regex: Regex,
55+
with replacement: Replacement,
56+
subrange: Range<Index>,
57+
maxReplacements: Int = .max
58+
) -> Self where Replacement.Element == Element {
59+
replacing(
60+
RegexConsumer(regex: regex),
61+
with: replacement,
62+
subrange: subrange,
63+
maxReplacements: maxReplacements)
64+
}
65+
66+
public func replacing<Replacement: Collection>(
67+
_ regex: Regex,
68+
with replacement: Replacement,
69+
maxReplacements: Int = .max
70+
) -> Self where Replacement.Element == Element {
71+
replacing(
72+
regex,
73+
with: replacement,
74+
subrange: startIndex..<endIndex,
75+
maxReplacements: maxReplacements)
76+
}
77+
78+
public mutating func replace<Replacement: Collection>(
79+
_ regex: Regex,
80+
with replacement: Replacement,
81+
maxReplacements: Int = .max
82+
) where Replacement.Element == Element {
83+
self = replacing(
84+
regex,
85+
with: replacement,
86+
maxReplacements: maxReplacements)
87+
}
88+
}

0 commit comments

Comments
 (0)