winstonp
(Winston)
June 18, 2022, 11:19am
1
Learning to use Swift, PATs and Generics. Regardless of performance, I would like to make it so in my code, I can perform string indexing like Python:
let text = "hello"
print(text[0..<2]) // "he"
I am trying to implement it as such:
import Foundation
extension String {
subscript (bounds: RangeExpression) -> String {
...
}
}
I've tried a lot of things, like:
subscript (bounds: RangeExpression<Int>
, but this is not allowed in Swift 5.6
define another protocol IntRange: RangeExpression
, but I did not figure out how to set its associated type
What is the right approach to this? Thanks!
tera
June 18, 2022, 11:44am
2
Will this work for you?
let text = "hello"
print(text[0..<2]) // "he"
print(text[0...2]) // "hel"
print(text[1...]) // "ello"
print(text[..<2]) // "he"
print(text[...2]) // "hel"
extension String {
subscript(bounds: Range<Int>) -> String {
String(self[self + bounds.lowerBound ..< self + bounds.upperBound])
}
subscript(bounds: ClosedRange<Int>) -> String {
String(self[self + bounds.lowerBound ... self + bounds.upperBound])
}
subscript(bounds: PartialRangeFrom<Int>) -> String {
String(self[self + bounds.lowerBound ..< endIndex])
}
subscript(bounds: PartialRangeUpTo<Int>) -> String {
String(self[startIndex ..< self + bounds.upperBound])
}
subscript(bounds: PartialRangeThrough<Int>) -> String {
String(self[startIndex ... self + bounds.upperBound])
}
static func + (string: Self, offset: Int) -> String.Index {
string.index(at: offset)
}
func index(at offset: Int) -> String.Index {
index(startIndex, offsetBy: offset)
}
}
BTW, you may return Substring
(and only convert to String
on the use side if needed) although if performance doesn't matter then returning String
is fine.
1 Like
winstonp
(Winston)
June 18, 2022, 1:07pm
3
Awesome, thanks! Out of curiosity, what exactly is the purpose of RangeExpression
then?
AlexanderM
(Alexander Momchilov)
June 18, 2022, 5:29pm
5
It's very intentionally omitted.
While the ergonomics aren't great as far as peoples usual expectations, indexing strings is actually a surprisingly uncommon operation in real world code (i.e. not a "shuffle the characters of a string" style code challenge). Most algorithms are better expressed in terms of higher level concepts like slicing, joining, taking prefixes, dropping prefixes, etc.
7 Likes
tera
June 18, 2022, 6:37pm
6
This should be a feature in Swift. Way easier to use.
Remember that index(_: String.Index, offsetBy:)
is O(n) operation. If you do it in a loop for all characters in the string - you have a quadratic algorithm.
We can have it in a form:
text[slow: 0...2]
"slow" should discourage people from using it in real world apps.
I am not sure . Will leave for others to comment upon.
3 Likes
RangeExpression
serves as an abstraction over all the range types in @tera ’s example code.
instead of providing overloads for Range
, ClosedRange
, PartialRangeFrom
, PartialRangeUpTo
, and PartialRangeThrough
, you could just provide one generic RangeExpression
method.
1 Like
tera
June 18, 2022, 8:05pm
8
Thank you, indeed:
let text = "hello"
print(text[slow: 0..<2]) // "he"
print(text[slow: 0...2]) // "hel"
print(text[slow: 1...]) // "ello"
print(text[slow: ..<2]) // "he"
print(text[slow: ...2]) // "hel"
extension String {
subscript<T: RangeExpression>(slow bounds: T) -> String where T.Bound == Int {
let range = bounds.relative(to: 0 ..< .max)
let left = index(startIndex, offsetBy: range.lowerBound)
let right = range.upperBound == .max ? endIndex : index(startIndex, offsetBy: range.upperBound)
return String(self[left ..< right])
}
}
1 Like