Skip to content

Commit 571e259

Browse files
authored
More updates for algorithms proposal (#324)
* Update Sequence/Collection polarity * Add a note about new generics features * Update split methods, catch some inconsistencies * Adopt opaque parameter types where possible today Also includes some generic naming updates and typo fixes. * Update documentation parameters * split(by:) -> split(separator:)
1 parent 70c0756 commit 571e259

File tree

1 file changed

+93
-55
lines changed

1 file changed

+93
-55
lines changed

Documentation/Evolution/StringProcessingAlgorithms.md

Lines changed: 93 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This proposal is part of a larger [regex-powered string processing initiative](h
1414

1515
## Motivation
1616

17-
A number of common string processing APIs are missing from the Swift standard library. While most of the desired functionalities can be accomplished through a series of API calls, every gap adds a burden to developers doing frequent or complex string processing. For example, here's one approach to find the number of occurrences a substring ("banana") within a string:
17+
A number of common string processing APIs are missing from the Swift standard library. While most of the desired functionalities can be accomplished through a series of API calls, every gap adds a burden to developers doing frequent or complex string processing. For example, here's one approach to find the number of occurrences of a substring ("banana") within a string:
1818

1919
```swift
2020
let str = "A banana a day keeps the doctor away. I love bananas; banana are my favorite fruit."
@@ -216,10 +216,10 @@ Matching and extracting a localized currency amount, such as `"$3,020.85"`, can
216216

217217
```swift
218218
let regex = Regex {
219-
capture(.localizedCurreny(code: "USD"))
219+
Capture(.localizedCurrency(code: "USD"))
220220
}
221221
```
222-
222+
223223
</details>
224224

225225

@@ -234,7 +234,7 @@ extension Collection where Element: Equatable {
234234
/// - Parameter other: A sequence to search for within this collection.
235235
/// - Returns: `true` if the collection contains the specified sequence,
236236
/// otherwise `false`.
237-
public func contains<S: Sequence>(_ other: S) -> Bool
237+
public func contains<C: Collection>(_ other: C) -> Bool
238238
where S.Element == Element
239239
}
240240

@@ -244,7 +244,7 @@ extension BidirectionalCollection where SubSequence == Substring {
244244
/// - Parameter regex: A regex to search for within this collection.
245245
/// - Returns: `true` if the regex was found in the collection, otherwise
246246
/// `false`.
247-
public func contains<R: RegexComponent>(_ regex: R) -> Bool
247+
public func contains(_ regex: some RegexComponent) -> Bool
248248
}
249249
```
250250

@@ -257,7 +257,7 @@ extension BidirectionalCollection where SubSequence == Substring {
257257
/// - Parameter regex: A regex to compare to this sequence.
258258
/// - Returns: `true` if the initial elements of the sequence matches the
259259
/// beginning of `regex`; otherwise, `false`.
260-
public func starts<R: RegexComponent>(with regex: R) -> Bool
260+
public func starts(with regex: some RegexComponent) -> Bool
261261
}
262262
```
263263

@@ -281,7 +281,7 @@ extension Collection where SubSequence == Self {
281281
/// - Parameter predicate: A closure that takes an element of the sequence
282282
/// as its argument and returns a Boolean value indicating whether the
283283
/// element should be removed from the collection.
284-
public mutating func trimPrefix(while predicate: (Element) throws -> Bool)
284+
public mutating func trimPrefix(while predicate: (Element) throws -> Bool) rethrows
285285
}
286286

287287
extension RangeReplaceableCollection {
@@ -290,7 +290,7 @@ extension RangeReplaceableCollection {
290290
/// - Parameter predicate: A closure that takes an element of the sequence
291291
/// as its argument and returns a Boolean value indicating whether the
292292
/// element should be removed from the collection.
293-
public mutating func trimPrefix(while predicate: (Element) throws -> Bool)
293+
public mutating func trimPrefix(while predicate: (Element) throws -> Bool) rethrows
294294
}
295295

296296
extension Collection where Element: Equatable {
@@ -299,21 +299,21 @@ extension Collection where Element: Equatable {
299299
/// - Parameter prefix: The collection to remove from this collection.
300300
/// - Returns: A collection containing the elements that does not match
301301
/// `prefix` from the start.
302-
public func trimmingPrefix<Prefix: Collection>(_ prefix: Prefix) -> SubSequence
302+
public func trimmingPrefix<Prefix: Sequence>(_ prefix: Prefix) -> SubSequence
303303
where Prefix.Element == Element
304304
}
305305

306306
extension Collection where SubSequence == Self, Element: Equatable {
307307
/// Removes the initial elements that matches `prefix` from the start.
308308
/// - Parameter prefix: The collection to remove from this collection.
309-
public mutating func trimPrefix<Prefix: Collection>(_ prefix: Prefix)
309+
public mutating func trimPrefix<Prefix: Sequence>(_ prefix: Prefix)
310310
where Prefix.Element == Element
311311
}
312312

313313
extension RangeReplaceableCollection where Element: Equatable {
314314
/// Removes the initial elements that matches `prefix` from the start.
315315
/// - Parameter prefix: The collection to remove from this collection.
316-
public mutating func trimPrefix<Prefix: Collection>(_ prefix: Prefix)
316+
public mutating func trimPrefix<Prefix: Sequence>(_ prefix: Prefix)
317317
where Prefix.Element == Element
318318
}
319319

@@ -323,15 +323,15 @@ extension BidirectionalCollection where SubSequence == Substring {
323323
/// - Parameter regex: The regex to remove from this collection.
324324
/// - Returns: A new subsequence containing the elements of the collection
325325
/// that does not match `prefix` from the start.
326-
public func trimmingPrefix<R: RegexComponent>(_ regex: R) -> SubSequence
326+
public func trimmingPrefix(_ regex: some RegexComponent) -> SubSequence
327327
}
328328

329329
extension RangeReplaceableCollection
330330
where Self: BidirectionalCollection, SubSequence == Substring
331331
{
332332
/// Removes the initial elements that matches the given regex.
333333
/// - Parameter regex: The regex to remove from this collection.
334-
public mutating func trimPrefix<R: RegexComponent>(_ regex: R)
334+
public mutating func trimPrefix(_ regex: some RegexComponent)
335335
}
336336
```
337337

@@ -344,8 +344,8 @@ extension Collection where Element: Equatable {
344344
/// - Parameter sequence: The sequence to search for.
345345
/// - Returns: A range in the collection of the first occurrence of `sequence`.
346346
/// Returns nil if `sequence` is not found.
347-
public func firstRange<S: Sequence>(of sequence: S) -> Range<Index>?
348-
where S.Element == Element
347+
public func firstRange<C: Collection>(of other: C) -> Range<Index>?
348+
where C.Element == Element
349349
}
350350

351351
extension BidirectionalCollection where Element: Comparable {
@@ -354,8 +354,8 @@ extension BidirectionalCollection where Element: Comparable {
354354
/// - Parameter other: The sequence to search for.
355355
/// - Returns: A range in the collection of the first occurrence of `sequence`.
356356
/// Returns `nil` if `sequence` is not found.
357-
public func firstRange<S: Sequence>(of other: S) -> Range<Index>?
358-
where S.Element == Element
357+
public func firstRange<C: BidirectionalCollection>(of other: C) -> Range<Index>?
358+
where C.Element == Element
359359
}
360360

361361
extension BidirectionalCollection where SubSequence == Substring {
@@ -364,7 +364,7 @@ extension BidirectionalCollection where SubSequence == Substring {
364364
/// - Parameter regex: The regex to search for.
365365
/// - Returns: A range in the collection of the first occurrence of `regex`.
366366
/// Returns `nil` if `regex` is not found.
367-
public func firstRange<R: RegexComponent>(of regex: R) -> Range<Index>?
367+
public func firstRange(of regex: some RegexComponent) -> Range<Index>?
368368
}
369369
```
370370

@@ -377,8 +377,8 @@ extension Collection where Element: Equatable {
377377
/// - Parameter other: The sequence to search for.
378378
/// - Returns: A collection of ranges of all occurrences of `other`. Returns
379379
/// an empty collection if `other` is not found.
380-
public func ranges<S: Sequence>(of other: S) -> some Collection<Range<Index>>
381-
where S.Element == Element
380+
public func ranges<C: Collection>(of other: C) -> some Collection<Range<Index>>
381+
where C.Element == Element
382382
}
383383

384384
extension BidirectionalCollection where SubSequence == Substring {
@@ -387,7 +387,7 @@ extension BidirectionalCollection where SubSequence == Substring {
387387
/// - Parameter regex: The regex to search for.
388388
/// - Returns: A collection or ranges in the receiver of all occurrences of
389389
/// `regex`. Returns an empty collection if `regex` is not found.
390-
public func ranges<R: RegexComponent>(of regex: R) -> some Collection<Range<Index>>
390+
public func ranges(of regex: some RegexComponent) -> some Collection<Range<Index>>
391391
}
392392
```
393393

@@ -399,17 +399,17 @@ extension BidirectionalCollection where SubSequence == Substring {
399399
/// - Parameter regex: The regex to search for.
400400
/// - Returns: The first match of `regex` in the collection, or `nil` if
401401
/// there isn't a match.
402-
public func firstMatch<R: RegexComponent>(of regex: R) -> RegexMatch<R.Match>?
402+
public func firstMatch<R: RegexComponent>(of regex: R) -> Regex<R.RegexOutput>.Match?
403403

404404
/// Match a regex in its entirety.
405-
/// - Parameter r: The regex to match against.
405+
/// - Parameter regex: The regex to match against.
406406
/// - Returns: The match if there is one, or `nil` if none.
407-
public func wholeMatch<R: RegexComponent>(of r: R) -> Regex<R.Output>.Match?
407+
public func wholeMatch<R: RegexComponent>(of regex: R) -> Regex<R.RegexOutput>.Match?
408408

409409
/// Match part of the regex, starting at the beginning.
410-
/// - Parameter r: The regex to match against.
410+
/// - Parameter regex: The regex to match against.
411411
/// - Returns: The match if there is one, or `nil` if none.
412-
public func prefixMatch<R: RegexComponent>(of r: R) -> Regex<R.Output>.Match?
412+
public func prefixMatch<R: RegexComponent>(of regex: R) -> Regex<R.RegexOutput>.Match?
413413
}
414414
```
415415

@@ -420,7 +420,7 @@ extension BidirectionalCollection where SubSequence == Substring {
420420
/// Returns a collection containing all matches of the specified regex.
421421
/// - Parameter regex: The regex to search for.
422422
/// - Returns: A collection of matches of `regex`.
423-
public func matches<R: RegexComponent>(of regex: R) -> some Collection<RegexMatch<R.Match>>
423+
public func matches<R: RegexComponent>(of regex: R) -> some Collection<Regex<R.RegexOuput>.Match>
424424
}
425425
```
426426

@@ -438,12 +438,12 @@ extension RangeReplaceableCollection where Element: Equatable {
438438
/// to replace. Default is `Int.max`.
439439
/// - Returns: A new collection in which all occurrences of `other` in
440440
/// `subrange` of the collection are replaced by `replacement`.
441-
public func replacing<S: Sequence, Replacement: Collection>(
442-
_ other: S,
441+
public func replacing<C: Collection, Replacement: Collection>(
442+
_ other: C,
443443
with replacement: Replacement,
444444
subrange: Range<Index>,
445445
maxReplacements: Int = .max
446-
) -> Self where S.Element == Element, Replacement.Element == Element
446+
) -> Self where C.Element == Element, Replacement.Element == Element
447447

448448
/// Returns a new collection in which all occurrences of a target sequence
449449
/// are replaced by another collection.
@@ -454,23 +454,23 @@ extension RangeReplaceableCollection where Element: Equatable {
454454
/// to replace. Default is `Int.max`.
455455
/// - Returns: A new collection in which all occurrences of `other` in
456456
/// `subrange` of the collection are replaced by `replacement`.
457-
public func replacing<S: Sequence, Replacement: Collection>(
458-
_ other: S,
457+
public func replacing<C: Collection, Replacement: Collection>(
458+
_ other: C,
459459
with replacement: Replacement,
460460
maxReplacements: Int = .max
461-
) -> Self where S.Element == Element, Replacement.Element == Element
461+
) -> Self where C.Element == Element, Replacement.Element == Element
462462

463463
/// Replaces all occurrences of a target sequence with a given collection
464464
/// - Parameters:
465465
/// - other: The sequence to replace.
466466
/// - replacement: The new elements to add to the collection.
467467
/// - maxReplacements: A number specifying how many occurrences of `other`
468468
/// to replace. Default is `Int.max`.
469-
public mutating func replace<S: Sequence, Replacement: Collection>(
470-
_ other: S,
469+
public mutating func replace<C: Collection, Replacement: Collection>(
470+
_ other: C,
471471
with replacement: Replacement,
472472
maxReplacements: Int = .max
473-
) where S.Element == Element, Replacement.Element == Element
473+
) where C.Element == Element, Replacement.Element == Element
474474
}
475475

476476
extension RangeReplaceableCollection where SubSequence == Substring {
@@ -484,8 +484,8 @@ extension RangeReplaceableCollection where SubSequence == Substring {
484484
/// sequence matching `regex` to replace. Default is `Int.max`.
485485
/// - Returns: A new collection in which all occurrences of subsequence
486486
/// matching `regex` in `subrange` are replaced by `replacement`.
487-
public func replacing<R: RegexComponent, Replacement: Collection>(
488-
_ r: R,
487+
public func replacing<Replacement: Collection>(
488+
_ r: some RegexComponent,
489489
with replacement: Replacement,
490490
subrange: Range<Index>,
491491
maxReplacements: Int = .max
@@ -500,8 +500,8 @@ extension RangeReplaceableCollection where SubSequence == Substring {
500500
/// sequence matching `regex` to replace. Default is `Int.max`.
501501
/// - Returns: A new collection in which all occurrences of subsequence
502502
/// matching `regex` are replaced by `replacement`.
503-
public func replacing<R: RegexComponent, Replacement: Collection>(
504-
_ r: R,
503+
public func replacing<Replacement: Collection>(
504+
_ r: some RegexComponent,
505505
with replacement: Replacement,
506506
maxReplacements: Int = .max
507507
) -> Self where Replacement.Element == Element
@@ -513,8 +513,8 @@ extension RangeReplaceableCollection where SubSequence == Substring {
513513
/// - replacement: The new elements to add to the collection.
514514
/// - maxReplacements: A number specifying how many occurrences of the
515515
/// sequence matching `regex` to replace. Default is `Int.max`.
516-
public mutating func replace<R: RegexComponent, Replacement: Collection>(
517-
_ r: R,
516+
public mutating func replace<Replacement: Collection>(
517+
_ r: some RegexComponent,
518518
with replacement: Replacement,
519519
maxReplacements: Int = .max
520520
) where Replacement.Element == Element
@@ -534,7 +534,7 @@ extension RangeReplaceableCollection where SubSequence == Substring {
534534
_ regex: R,
535535
subrange: Range<Index>,
536536
maxReplacements: Int = .max,
537-
with replacement: (RegexMatch<R.Match>) throws -> Replacement
537+
with replacement: (Regex<R.RegexOutput>.Match) throws -> Replacement
538538
) rethrows -> Self where Replacement.Element == Element
539539

540540
/// Returns a new collection in which all occurrences of a sequence matching
@@ -550,7 +550,7 @@ extension RangeReplaceableCollection where SubSequence == Substring {
550550
public func replacing<R: RegexComponent, Replacement: Collection>(
551551
_ regex: R,
552552
maxReplacements: Int = .max,
553-
with replacement: (RegexMatch<R.Match>) throws -> Replacement
553+
with replacement: (Regex<R.RegexOuput>.Match) throws -> Replacement
554554
) rethrows -> Self where Replacement.Element == Element
555555

556556
/// Replaces all occurrences of the sequence matching the given regex with
@@ -564,7 +564,7 @@ extension RangeReplaceableCollection where SubSequence == Substring {
564564
public mutating func replace<R: RegexComponent, Replacement: Collection>(
565565
_ regex: R,
566566
maxReplacements: Int = .max,
567-
with replacement: (RegexMatch<R.Match>) throws -> Replacement
567+
with replacement: (Regex<R.RegexOutput>.Match) throws -> Replacement
568568
) rethrows where Replacement.Element == Element
569569
}
570570
```
@@ -574,27 +574,65 @@ extension RangeReplaceableCollection where SubSequence == Substring {
574574
```swift
575575
extension Collection where Element: Equatable {
576576
/// Returns the longest possible subsequences of the collection, in order,
577-
/// around elements equal to the given separator.
578-
/// - Parameter separator: The element to be split upon.
577+
/// around elements equal to the given separator collection.
578+
///
579+
/// - Parameters:
580+
/// - separator: A collection of elements to be split upon.
581+
/// - maxSplits: The maximum number of times to split the collection,
582+
/// or one less than the number of subsequences to return.
583+
/// - omittingEmptySubsequences: If `false`, an empty subsequence is
584+
/// returned in the result for each consecutive pair of separator
585+
/// sequences in the collection and for each instance of separator
586+
/// sequences at the start or end of the collection. If `true`, only
587+
/// nonempty subsequences are returned.
579588
/// - Returns: A collection of subsequences, split from this collection's
580-
/// elements.
581-
public func split<S: Sequence>(by separator: S) -> some Collection<SubSequence>
582-
where S.Element == Element
589+
/// elements.
590+
public func split<C: Collection>(
591+
separator: C,
592+
maxSplits: Int = Int.max,
593+
omittingEmptySubsequences: Bool = true
594+
) -> some Collection<SubSequence> where C.Element == Element
583595
}
584596

585597
extension BidirectionalCollection where SubSequence == Substring {
586598
/// Returns the longest possible subsequences of the collection, in order,
587-
/// around elements equal to the given separator.
588-
/// - Parameter separator: A regex describing elements to be split upon.
599+
/// around subsequence that match the given separator regex.
600+
///
601+
/// - Parameters:
602+
/// - separator: A regex to be split upon.
603+
/// - maxSplits: The maximum number of times to split the collection,
604+
/// or one less than the number of subsequences to return.
605+
/// - omittingEmptySubsequences: If `false`, an empty subsequence is
606+
/// returned in the result for each consecutive pair of matches
607+
/// and for each match at the start or end of the collection. If
608+
/// `true`, only nonempty subsequences are returned.
589609
/// - Returns: A collection of substrings, split from this collection's
590-
/// elements.
591-
public func split<R: RegexComponent>(by separator: R) -> some Collection<Substring>
610+
/// elements.
611+
public func split(
612+
separator: some RegexComponent,
613+
maxSplits: Int = Int.max,
614+
omittingEmptySubsequences: Bool = true
615+
) -> some Collection<Substring>
592616
}
593617
```
594618

619+
**Note:** We plan to adopt the new generics features enabled by [SE-0346][] for these proposed methods when the standard library adopts primary associated types, [pending a forthcoming proposal][stdlib-pitch]. For example, the first method in the _Replacement_ section above would instead be:
595620

621+
```swift
622+
extension RangeReplaceableCollection where Element: Equatable {
623+
/// Returns a new collection in which all occurrences of a target sequence
624+
/// are replaced by another collection.
625+
public func replacing(
626+
_ other: some Collection<Element>,
627+
with replacement: some Collection<Element>,
628+
subrange: Range<Index>,
629+
maxReplacements: Int = .max
630+
) -> Self
631+
}
632+
```
596633

597-
634+
[SE-0346]: https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md
635+
[stdlib-pitch]: https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426
598636

599637
## Alternatives considered
600638

0 commit comments

Comments
 (0)