Skip to content

Instantly share code, notes, and snippets.

@CTMacUser
Created February 1, 2019 05:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CTMacUser/be95667443d7e6c2b54f3de8ae27e153 to your computer and use it in GitHub Desktop.
Save CTMacUser/be95667443d7e6c2b54f3de8ae27e153 to your computer and use it in GitHub Desktop.
A sequence that strides over most of its wrapped sequence's elements.
//
// StridingSequence.swift
// CLITest
//
// Created by Daryle Walker on 1/31/19.
// Copyright © 2019 Daryle Walker. All rights reserved.
//
// WARNING: I think this requires Swift 5, since it needs Sequence to no longer have an SubSequence member.
/**
The iterator for `StridingSequence`. It filters out all of its wrapped iterator's elements except the ones at every *n* steps, with the first immediately available element the first one to keep.
*/
public struct StridingIterator<Base: IteratorProtocol> {
/// The wrapped iterator; source of the elements to be vended.
var base: Base
/// The spacing between elements of the source iterator to be vended.
let stride: Int
/**
Creates an iterator wrapping the given one, filtering in only the elements with the given spacing.
- Precondition: `stride > 0`.
- Parameter base: The iterator to copy as a source of elements.
- Parameter stride: The cycle span of elements to filter in.
- Postcondition: On every call to `next()`, this iterator will vend out the immediately upcoming element from `base`, then purge the following `stride - 1` elements.
*/
init(_ base: Base, strideOf stride: Int) {
precondition(stride > 0)
self.base = base
self.stride = stride
}
}
extension StridingIterator: IteratorProtocol {
mutating public func next() -> Base.Element? {
defer {
for _ in 1..<stride {
_ = base.next()
}
}
return base.next()
}
}
/// A sequence that filters out all the elements of its wrapped sequence except the ones whose release-order offset matches a cycle.
public struct StridingSequence<Base: Sequence> {
/// The wrapped sequence; source of the elements to be vended.
let base: Base
/// The spacing between elements of the source sequence to be vended.
let stride: Int
/**
Creates a sequence wrapping the given one, filtering in only the elements with the given spacing.
- Precondition: `stride > 0`.
- Parameter base: The sequence to copy as a source of elements.
- Parameter stride: The cycle span of elements to filter in.
- Postcondition: Represents an underlying sequence of the elements of `base` whose release offsets are multiples of `stride`.
*/
@usableFromInline
init(_ base: Base, strideOf stride: Int) {
precondition(stride > 0)
self.base = base
self.stride = stride
}
}
extension StridingSequence: Sequence {
public __consuming func makeIterator() -> StridingIterator<Base.Iterator> {
return StridingIterator(base.makeIterator(), strideOf: stride)
}
public var underestimatedCount: Int {
let (ucq, ucr) = base.underestimatedCount.quotientAndRemainder(dividingBy: stride)
return ucq + ucr.signum()
}
}
extension StridingSequence: Collection where Base: Collection {
public typealias Index = Base.Index
public var startIndex: Base.Index { return base.startIndex }
public var endIndex: Base.Index { return base.endIndex }
public subscript(position: Base.Index) -> Base.Element { return base[position] }
public func index(after i: Base.Index) -> Base.Index {
precondition(i < endIndex)
return base.index(i, offsetBy: stride, limitedBy: base.endIndex) ?? endIndex
}
public var isEmpty: Bool { return base.isEmpty }
public var count: Int {
let (cq, cr) = base.count.quotientAndRemainder(dividingBy: stride)
return cq + cr.signum()
}
public func distance(from start: Base.Index, to end: Base.Index) -> Int {
let (dq, dr) = base.distance(from: start, to: end).quotientAndRemainder(dividingBy: stride)
return dq + dr.signum()
}
}
extension StridingSequence: BidirectionalCollection, RandomAccessCollection where Base: RandomAccessCollection {
public func index(before i: Base.Index) -> Base.Index {
guard i == base.endIndex, case let shortOffset = base.count % stride, shortOffset != 0 else {
return base.index(i, offsetBy: -stride)
}
return base.index(i, offsetBy: -shortOffset)
}
public func index(_ i: Base.Index, offsetBy n: Int) -> Base.Index {
guard n != 0 else { return i }
// From now on, we can't avoid choking if isEmpty, so cause that crash ASAP.
let lastIndex = index(before: endIndex)
guard let result = base.index(i, offsetBy: n * stride, limitedBy: lastIndex) else {
// The offset jumps across the lastIndex boundary. Rebase from something on the same side.
let iIsEndIndex = i > lastIndex
let shiftBase = iIsEndIndex ? lastIndex : endIndex
let shiftOffset = iIsEndIndex ? 0 : 1
return base.index(shiftBase, offsetBy: (n - distance(from: i, to: lastIndex) - shiftOffset) * stride)
}
return result
}
}
/// A collection that filters out all the elements of its wrapped collection except the ones whose index offset matches a cycle.
typealias StridingCollection<T: Collection> = StridingSequence<T>
extension Sequence {
/**
Returns a sequence consisting of the first element of this sequence followed by those found by stepping by a specified amount of elements past the last released element.
The returned sequence is a customized version of:
enumerated().fliter { $0.0.isMultiple(of: stride) }.map { $0.1 }
The number of elements in the returned sequence depends on if this sequence's element count is a multiple of the `stride`.
let s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(Array(s.filterIn(afterStepsOf: 5)))
// [1, 6]
print(Array(s.filterIn(afterStepsOf: 3)))
// [1, 4, 7, 10]
- Precondition: `stride > 0`.
- Parameter stride: The enumerated counter offsets elements had between each other in `self`.
*/
@inlinable
public __consuming func filterIn(afterStepsOf stride: Int) -> StridingSequence<Self> {
return StridingSequence(self, strideOf: stride)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment