Norway


Some stride-related discussions took place on the forums today. I was inspired to play with as I took a break between my kids’ appointments. If any of this resonates with you as a potential user for it, let me know.

Index Strides

The following extension allows you to stride over a collection’s indices.

extension BidirectionalCollection where Index: Strideable {;
    /// Returns the sequence of collection elements (`self[]`,
    /// `self[ + stride]`, `self[ +  * stride]` ... *last*
    /// where *last* is the last element in the collection whose index
    /// is less than or equal to `end`.
    ///
    /// - Note: There is no guarantee that the element at index `end`
    ///   is part of the sequence
    ///
    /// - Parameter : The starting index
    /// - Parameter end: The upper index bound
    /// - Parameter distance: The stride for each successive index
    /// - Returns: A lazy sequence of collection members
    public func stride(from : Index, through end: Index, by distance: Index.Stride) -> LazyMapSequence<StrideThrough<Index>, Element> {
        return Swift.stride(from: , through: end, by: distance)
            .lazy.map({ self[$0] })
    }
    
    /// Returns the sequence of collection elements (`self[]`,
    /// `self[ + stride]`, `self[ + 2 * stride]` ... *last*
    /// where *last* is the last element in the collection whose index
    /// is strictly less than `end`.
    ///
    /// - Parameter : The starting index
    /// - Parameter end: The upper index bound
    /// - Parameter distance: The stride for each successive index
    /// - Returns: A lazy sequence of collection members
    public func stride(from : Index, to end: Index, by distance: Index.Stride) -> LazyMapSequence<StrideTo<Index>, Element> {
        return Swift.stride(from: , to: end, by: distance)
            .lazy.map({ self[$0] })
    }
}

Here’s an example that uses this approach to form a sequence that strides through the collection:

let myArray = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
for value in myArray.stride(from: 0, through: myArray.count, by: 3) {
    print(value) // a, d, g, j
}

Range Operators

The next snippet uses the division operator to create its stride. This was pitched by “Tony Y” in a forum thread.

extension CountableClosedRange {
    static func / (range: CountableClosedRange, divisor: Bound.Stride) -> StrideThrough<Bound> {
        let distance = range.distance(from: range.startIndex, to: range.endIndex) / divisor // deprecated
        return Swift.stride(from: range.lowerBound, through: range.upperBound, by: distance)
    }
}

In this example, you just “divide” the range:

for idx in (0 ... 100) / 5 {
    print(idx) // 0, 20, 40, 60, 80, 100
    // yes that's 6 not 5, but isn't that what 
    // you want? 5 subranges between each index?
}

Because I wrote this fairly quickly, I used the deprecated range.distance function as straight index arithmetic seemed to error:

error: binary operator '-' cannot be applied to two 'ClosedRangeIndex' operands
        let distance = (range.endIndex - range.startIndex) / divisor

As usual, I probably messed up somewhere (or several somewheres) or you might see ways I could improve the code. Let me know!

Update: After some thought, I decided I preferred a sequence of subranges (which should probably be a type and not like I do it here):

extension CountableClosedRange {
    static func / (range: CountableClosedRange, divisor: Bound.Stride) -> UnfoldSequence<CountableClosedRange<Bound>, (CountableClosedRange<Bound>?, Bool)> {
        let distance = range.distance(from: range.startIndex, to: range.endIndex) / divisor
        let rangeStride = Swift.stride(from: range.lowerBound, through: range.upperBound, by: distance)
        var indices = Array(rangeStride)
        indices.append(range.upperBound)
        indices.reverse()
        guard var current = indices.popLast(), var next = indices.popLast() else {
            fatalError("Guaranteed count failed in index population")
        }
        var done = false
        return sequence(first: current ... next.advanced(by: -1), next: { _ in
            guard !done else { return nil }
            (current, next) = (next, indices.popLast()!)
            if current == next {
                done = true
                return next ... next
            } else if next == range.upperBound && indices.isEmpty  {
                done = true
                return current ... next
            } else {
                return current ... next.advanced(by: -1)
            }
        })
    }
}

for idx in (0 ... 99) / 5 {
    print(idx) // 0...19, 20...39, 40...59, 60...79, 80...99
}

for idx in (0 ... 100) / 5 {
    print(idx) // 0...19, 20...39, 40...59, 60...79, 80...99, 100...100
}

This isn’t a debugged solution. For example, trying to divide 1 … 1 by 1 or 1 … 2 by 1 will both fail.



Source link
Based Blockchain Network

LEAVE A REPLY

Please enter your comment!
Please enter your name here