Created
February 1, 2019 05:26
-
-
Save CTMacUser/be95667443d7e6c2b54f3de8ae27e153 to your computer and use it in GitHub Desktop.
A sequence that strides over most of its wrapped sequence's elements.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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