[swift-evolution] [Discussion][Expericment] D-like slicing syntax

Daniel Duan daniel at duan.org
Sat Feb 20 18:41:54 CST 2016


Hi all,

A while ago there's a thread on adding Python's slicing syntax here:
http://thread.gmane.org/gmane.comp.lang.swift.evolution/124/focus=261

>From it, a brief summary of the issues in Python's slicing syntax:

1. light syntax may suggest O(1) operation complexity to some.
2. negative index imposes runtime overhead
3. negative index masks fencepost errors (`foo(m-n)` supprises if you assume
   m > n)

D's slicing syntax (https://dlang.org/d-array-article.html) was brought up as
an alternative. It does a better job in suggesting "unusual" operation (if you
treat subscripting with an index as normal). I attached a rudimentary
implementation of it at the end. It's in not way aimed at being complete or
ending up in stdlib. Hopefully, being able to play with it will generate more
discussion on this topic :)


// (if you prefer a gist: https://gist.github.com/dduan/5c54df865fdd7b6b7548)
//
// Incomplete:
//   '%' replaces the more ideal '$' (the latter can't be in name of operator)
//   where a open ended range is in the design, `nil` is used its place
//   force casting between Index.Distance and 'Int'
//   only on CollectionType, but a version for string is not hard to add

public struct Offset { let value: Int }

prefix operator %+ {}
prefix operator %- {}

prefix func %+(offset: Int) -> Offset {
    return Offset(value: offset)
}
prefix func %-(offset: Int) -> Offset {
    return Offset(value: -offset)
}

internal func normalize(start: Int?, end: Int?, total: Int) -> (Int, Int) {
    var actualStart = start ?? 0
    var actualEnd = end ?? total
    actualStart = actualStart < 0 ? total + actualStart : actualStart
    actualEnd = actualEnd < 0 ? total + actualEnd : actualEnd
//  uncomment the following for a much more forgiving index behavior
//  actualStart = actualStart < 0 ? 0 : actualStart
//  actualEnd = actualEnd < 0 ? 0 : actualEnd
//  let safeEnd = min(actualEnd, total)
//  return (min(actualStart, safeEnd), safeEnd)
    return (actualStart, actualEnd)
}

extension CollectionType {
    public subscript(start:Offset?, end:Offset?) -> Self.SubSequence {
        let (safeStart, safeEnd) = normalize(
            start?.value, end: end?.value, total: self.count as! Int)
        let safeStartIndex = startIndex.advancedBy(
            safeStart as! Self.Index.Distance)
        let safeEndIndex = startIndex.advancedBy(
            safeEnd as! Self.Index.Distance)
        return self[safeStartIndex..<safeEndIndex]
    }

    public subscript(index: Offset) -> Self.Generator.Element {
        let (safeStart, _) = normalize(
            index.value, end: 0, total: self.count as! Int)
        return self[startIndex.advancedBy(safeStart as! Self.Index.Distance)]
    }
}


let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

            // equavelent in Swift            Python     Result
s[%+2]      // s[s.startIndex.advancedBy(2)]  s[2]       2
s[%-2]      // s[s.endIndex.advancedBy(-2)]   s[-2]      8
s[%+2, %-3] // s.dropFirst(2).dropLast(3)     s[2:-2]    [2, 3, 4, 5, 6]
s[%-5, nil] // s.dropFirst(s.count - 5)       s[-5:]     [5, 6, 7, 8, 9]
s[nil, %-8] // s.dropLast(8)                  s[:-8]     [0, 1]




More information about the swift-evolution mailing list