You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Empty matches and inputs are an important edge casefor several of the algorithms proposed above. For example, what is the result of `"123.firstRange(of: /[a-z]*/)`? How do you split a collection separated by an empty collection, as in `"1234".split(separator: "")`? For the Swift standard library, this is a new consideration, as current algorithms are `Element`-based and cannot be passed an empty input.
1072
+
1073
+
Languages and libraries are nearly unanimous about finding the location of an empty string, with Ruby, Python, C#, Java, Javascript, etc, finding an empty string at each index in the target. Notably, Foundation's `NSString.range(of:)` does _not_ find an empty string at all.
1074
+
1075
+
The methods proposed here follow the consensus behavior, which makes sense if you think of `a.firstRange(of: b)` as returning the first subrange `r` where `a[r] == b`. If a regex can match an empty substring, like `/[a-z]*/`, the behavior is the same.
1076
+
1077
+
```swift
1078
+
let hello = "Hello"
1079
+
let emptyRange = hello.firstRange(of: "")
1080
+
// emptyRange is equivalent to '0..<0' (integer ranges shown for readability)
1081
+
```
1082
+
1083
+
Because searching again at the same index would yield that same empty string, we advance one position after finding an empty string or matching an empty pattern when finding all ranges. This yields the position of every valid index in the string.
1084
+
1085
+
```swift
1086
+
let allRanges = hello.ranges(of: "")
1087
+
// allRanges is equivalent to '[0..<0, 1..<1, 2..<2, 3..<3, 4..<4, 5..<5]'
1088
+
```
1089
+
1090
+
Splitting with an empty separator (or a pattern that matches empty string), uses this same behavior, resulting in a collection of single-element substrings. Interestingly, a couple languages make different choices here. C# returns the original string instead of its parts, and Python rejects an empty separator (though it permits regexes that match empty strings).
1091
+
1092
+
```swift
1093
+
let parts = hello.split(separator: "")
1094
+
// parts == ["h", "e", "l", "l", "o"]
1095
+
1096
+
let moreParts = hello.split(separator: "", omittingEmptySubsequences: false)
1097
+
// parts == ["", "h", "e", "l", "l", "o", ""]
1098
+
```
1099
+
1100
+
Finally, searching for an empty string within an empty string yields, as you might imagine, the empty string:
1101
+
1102
+
```swift
1103
+
let empty = ""
1104
+
let range = empty.firstRange(of: empty)
1105
+
// empty == empty[range]
1106
+
```
1107
+
1069
1108
## Alternatives considered
1070
1109
1071
1110
### Extend `Sequence` instead of `Collection`
1072
1111
1073
-
Most of the proposed algorithms are necessarily on `Collection` due to the use of indices or mutation. `Sequence` does not support multi-pass iteration, so even `trimPrefix` would problematic on `Sequence` because it needs to look 1 `Element` ahead to know when to stop trimming.
1112
+
Most of the proposed algorithms are necessarily on `Collection` due to the use of indices or mutation. `Sequence` does not support multi-pass iteration, so even `trimmingPrefix` would problematic on `Sequence` because it needs to look one `Element` ahead to know when to stop trimming and would need to return a wrapper for the in-progress iterator instead of a subsequence.
0 commit comments