> On Jun 8, 2017, at 10:32 AM, David Hart via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Hello,
> 
> When working with Strings which are known to be ASCII, I tend to use the 
> UTF16View for the performance of random access. I would also like to have the 
> convenience of indexing with Int:
> 
> let barcode = "M1XXXXXXXXX/CLEMENT   EELT9QBQGVAAMSEZY1353 244 21D 531  
> 10A1311446838”
> let name = barcode.utf16[2..<22]
> let pnrCode = barcode.utf16[23..<30]
> let seatNo = barcode.utf16[47..<51]
> let fromCity = barcode.utf16[30..<33]
> let toCity = barcode.utf16[33..<36]
> let carrier = barcode.utf16[36..<39]
> let flightNumber = barcode.utf16[39..<44]
> let day = barcode.utf16[44..<47]
> 
> I define my own subscript in an extension to UTF16View but I think this 
> should go in the Standard Library.
> Any thoughts?
> 
> David.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


Hi David,

My view is positional shredding of strings is enough of a use case that we 
ought to think about handling it better. I don’t think having to convert the 
string into an Array of Character, or bytes, or use Data, should be necessary, 
since this implies losing the stringiness of the slices you are creating, which 
is an inconvenience for many use cases. Strings-as-data is a thing we should 
support, and support well.

But I also don’t think giving String or its views integer indices is the right 
way to go either, incorrectly implying as it does random access 

(or, even if we did end up making utf16 permanently random-access, encouraging 
people towards using utf16 to support this use case when often they’d be better 
served sticking with characters).

There’s a few things to note about the example you give:
1) Do you really want utf16 view slices for your variable types? Maybe in this 
case you do, but I would guess a lot of the time what is desired would be a 
(sub)string.
2) Do you really want to hard-code integer literals into your code? Maybe for a 
quick shell script use case, but for anything more this seems like an 
anti-pattern that integer indices encourage.
3) Are you likely to actually want validation at each step – that the string 
was long enough, that the string data was valid at that point?
4) This case doesn’t seem to need random access particularly, so much as the 
(questionable? see above) convenience of integer indexing. Although reordered 
in the example, it seems like the code could be written to progressively 
traverse the string from left to right to get the fields. Unless the plan is to 
repeatedly access some fields over and over. But I’m not sure we’d want to put 
affordances into the std lib to encourage accessing stringly typed data...

So the question is, what affordances should we put into the standard library, 
or maybe other libraries, to help with these use cases? This is a big design 
space to explore, and at least some ideas ought to feed into our ongoing 
improvements to String for future releases.

For example:

Now that we have Substring, it opens up the ability to efficiently consume from 
the front (since it’s just adjusting the substring range):

extension Collection where Self == SubSequence {
    // or some better name...
    mutating func removeFirst(_ n: IndexDistance) -> SubSequence? {
        guard let i = index(startIndex, offsetBy: n, limitedBy: endIndex)
            else { return nil }
        
        defer { self = self[i...] }
        return self[..<i]
    }
}

Once you have this, you could use it to write the example code, along with some 
error checking (or you could use ! if you were completely certain of the 
integrity of your data)

var s = barcode[...] // make a substring for efficient consumption
_ = s.consume(2)     // drop initial prefix
guard let name = s.removeFirst(20)?.trim else { fatalError("Failed to read 
name") }
guard let pnrCode =  s.removeFirst(6).map(PNRCode.init) else { 
fatalError("Failed to read pnrCode") }

Or you could build on the above to create a function that could shred a string 
into a dictionary of fields… (probably a bit domain-specific for the std lib at 
this point)

extension Collection {
    // could choose to handle or fail on gaps, out-of-order ranges, overlapping 
ranges etc
    func fields<P: Collection>(at positions: P) -> [String:SubSequence]? // or 
throw an error with a field name
    where P.Element == (key: String, value: CountableRange<IndexDistance>)
}

let barcodeSchema: DictionaryLiteral = [
    "name": 2..<22,
    "pnrCode": 23..<30,
    "fromCity": 30..<33,
    "toCity": 33..<36,
    "carrier": 36..<39,
    "flightNumber": 39..<44,
    "day": 45..<47,
    "seatNo": 47..<51,
]

let fields = barcode.fields(at: barcodeSchema)!

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to