Files
swift-mirror/stdlib/public/core/Flatten.swift.gyb
2016-03-14 03:04:02 -05:00

382 lines
12 KiB
Swift

//===--- Flatten.swift.gyb ------------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// An iterator that produces the elements contained in each segment
/// produced by some `Base` Iterator.
///
/// The elements traversed are the concatenation of those in each
/// segment produced by the base iterator.
///
/// - Note: This is the `IteratorProtocol` used by `FlattenSequence`,
/// `FlattenCollection`, and `BidirectionalFlattenCollection`.
public struct FlattenIterator<
Base : IteratorProtocol where Base.Element : Sequence
> : IteratorProtocol, Sequence {
/// Construct around a `base` iterator.
internal init(_base: Base) {
self._base = _base
}
/// Advance to the next element and return it, or `nil` if no next
/// element exists.
///
/// - Precondition: `next()` has not been applied to a copy of `self`
/// since the copy was made, and no preceding call to `self.next()`
/// has returned `nil`.
public mutating func next() -> Base.Element.Iterator.Element? {
repeat {
if _fastPath(_inner != nil) {
let ret = _inner!.next()
if _fastPath(ret != nil) {
return ret
}
}
let s = _base.next()
if _slowPath(s == nil) {
return nil
}
_inner = s!.makeIterator()
}
while true
}
internal var _base: Base
internal var _inner: Base.Element.Iterator?
}
/// A sequence consisting of all the elements contained in each segment
/// contained in some `Base` sequence.
///
/// The elements of this view are a concatenation of the elements of
/// each sequence in the base.
///
/// The `flatten` property is always lazy, but does not implicitly
/// confer laziness on algorithms applied to its result. In other
/// words, for ordinary sequences `s`:
///
/// * `s.flatten()` does not create new storage
/// * `s.flatten().map(f)` maps eagerly and returns a new array
/// * `s.lazy.flatten().map(f)` maps lazily and returns a `LazyMapSequence`
///
/// - See also: `FlattenCollection`
public struct FlattenSequence<
Base : Sequence where Base.Iterator.Element : Sequence
> : Sequence {
/// Creates a concatenation of the elements of the elements of `base`.
///
/// - Complexity: O(1)
internal init(_base: Base) {
self._base = _base
}
/// Returns an iterator over the elements of this sequence.
///
/// - Complexity: O(1).
public func makeIterator() -> FlattenIterator<Base.Iterator> {
return FlattenIterator(_base: _base.makeIterator())
}
internal var _base: Base
}
extension Sequence where Iterator.Element : Sequence {
/// A concatenation of the elements of `self`.
@warn_unused_result
public func flatten() -> FlattenSequence<Self> {
return FlattenSequence(_base: self)
}
}
extension LazySequenceProtocol
where
Elements.Iterator.Element == Iterator.Element,
Iterator.Element : Sequence {
/// A concatenation of the elements of `self`.
@warn_unused_result
public func flatten() -> LazySequence<
FlattenSequence<Elements>
> {
return FlattenSequence(_base: elements).lazy
}
}
%{
def collectionForTraversal(traversal):
if traversal == 'Forward':
return 'Collection'
if traversal == 'Bidirectional':
return 'BidirectionalCollection'
if traversal == 'RandomAccess':
return 'RandomAccessCollection'
assert False, "unknown traversal"
}%
%{
def indexConfromsToForTraversal(traversal):
if traversal == 'Forward':
return 'Comparable'
if traversal == 'Bidirectional':
return 'Comparable'
if traversal == 'RandomAccess':
return 'Strideable'
assert False, "unknown traversal"
}%
% for traversal in ['Forward', 'Bidirectional']:
% t = '' if traversal == 'Forward' else traversal
% Collection = 'Flatten%sCollection' % t
% if traversal == 'Forward':
% constraints = '%(Base)sIterator.Element : Collection'
% if traversal == 'Bidirectional':
% constraints = '%(Base)sIterator.Element : BidirectionalCollection'
% Index = Collection + 'Index'
/// A position in a `${Collection}`.
public struct ${Index}<
BaseElements : ${collectionForTraversal(traversal)}
where
${constraints % {'Base': 'BaseElements.'}}
> : ${indexConfromsToForTraversal(traversal)} {
internal init(
_ _outer: BaseElements.Index,
_ inner: BaseElements.Iterator.Element.Index?) {
self._outer = _outer
self._inner = inner
}
internal let _outer: BaseElements.Index
internal let _inner: BaseElements.Iterator.Element.Index?
}
@warn_unused_result
public func == <BaseElements> (
lhs: ${Index}<BaseElements>,
rhs: ${Index}<BaseElements>
) -> Bool {
return lhs._outer == rhs._outer && lhs._inner == rhs._inner
}
@warn_unused_result
public func < <BaseElements> (
lhs: ${Index}<BaseElements>,
rhs: ${Index}<BaseElements>
) -> Bool {
if lhs._outer != rhs._outer {
return lhs._outer < rhs._outer
}
if let lhsInner = lhs._inner, rhsInner = rhs._inner {
return lhsInner < rhsInner
}
// When combined, the two conditions above guarantee that both
// `_outer` indices are `_base.endIndex` and both `_inner` indices
// are `nil`, since `_inner` is `nil` iff `_outer == base.endIndex`.
_precondition(lhs._inner == nil && rhs._inner == nil)
return false
}
/// A flattened view of a base collection-of-collections.
///
/// The elements of this view are a concatenation of the elements of
/// each collection in the base.
///
/// The `flatten` property is always lazy, but does not implicitly
/// confer laziness on algorithms applied to its result. In other
/// words, for ordinary collections `c`:
///
/// * `c.flatten()` does not create new storage
/// * `c.flatten().map(f)` maps eagerly and returns a new array
/// * `c.lazy.flatten().map(f)` maps lazily and returns a `LazyMapCollection`
///
/// - Note: The performance of accessing `startIndex`, `first`, any methods
/// that depend on `startIndex`, or of advancing a `${Collection}Index`
/// depends on how many empty subcollections are found in the base
/// collection, and may not offer the usual performance given by
/// `CollectionType` or `${traversal}IndexType`. Be aware, therefore, that
/// general operations on `${Collection}` instances may not have the
/// documented complexity.
///
/// - See also: `FlattenSequence`
public struct ${Collection}<
Base : ${collectionForTraversal(traversal)}
where
${constraints % {'Base': 'Base.'}}
> : ${collectionForTraversal(traversal)} {
/// A type that represents a valid position in the collection.
///
/// Valid indices consist of the position of every element and a
/// "past the end" position that's not valid for use as a subscript.
public typealias Index = ${Index}<Base>
public typealias IndexDistance = Base.IndexDistance
/// Creates a flattened view of `base`.
public init(_ base: Base) {
self._base = base
}
/// Returns an iterator over the elements of this sequence.
///
/// - Complexity: O(1).
public func makeIterator() -> FlattenIterator<Base.Iterator> {
return FlattenIterator(_base: _base.makeIterator())
}
/// The position of the first element in a non-empty collection.
///
/// In an empty collection, `startIndex == endIndex`.
public var startIndex: Index {
var outer = _base.startIndex
while outer != _base.endIndex {
let innerCollection = _base[outer]
if !innerCollection.isEmpty {
return ${Index}(outer, innerCollection.startIndex)
}
_base._nextInPlace(&outer)
}
return endIndex
}
/// The collection's "past the end" position.
///
/// `endIndex` is not a valid argument to `subscript`, and is always
/// reachable from `startIndex` by zero or more applications of
/// `successor()`.
public var endIndex: Index {
return ${Index}(_base.endIndex, nil)
}
// TODO: swift-3-indexing-model - add docs
@warn_unused_result
public func next(i: Index) -> Index {
// FIXME: swift-3-indexing-model: range checks?
let nextInner = _base[i._outer].next(i._inner!)
if _fastPath(nextInner != _base[i._outer].endIndex) {
return ${Index}(i._outer, nextInner)
}
var nextOuter = _base.next(i._outer)
while nextOuter != _base.endIndex {
let innerCollection = _base[nextOuter]
if !innerCollection.isEmpty {
return ${Index}(nextOuter, innerCollection.startIndex)
}
_base._nextInPlace(&nextOuter)
}
return endIndex
}
% if traversal == 'Bidirectional':
// TODO: swift-3-indexing-model - add docs
@warn_unused_result
public func previous(i: Index) -> Index {
// FIXME: swift-3-indexing-model: range checks?
var prevOuter = i._outer
if prevOuter == _base.endIndex {
prevOuter = _base.previous(prevOuter)
}
var prevInnerCollection = _base[prevOuter]
var prevInner = i._inner ?? prevInnerCollection.endIndex
while prevInner == prevInnerCollection.startIndex {
prevOuter = _base.previous(prevOuter)
prevInnerCollection = _base[prevOuter]
prevInner = prevInnerCollection.endIndex
}
return ${Index}(prevOuter, prevInnerCollection.previous(prevInner))
}
% end
/// Access the element at `position`.
///
/// - Precondition: `position` is a valid position in `self` and
/// `position != endIndex`.
public subscript(
position: Index
) -> Base.Iterator.Element.Iterator.Element {
return _base[position._outer][position._inner!]
}
// To return any estimate of the number of elements, we have to start
// evaluating the collections. That is a bad default for `flatMap()`, so
// just return zero.
public var underestimatedCount: Int { return 0 }
public func _copyToNativeArrayBuffer()
-> _ContiguousArrayBuffer<Base.Iterator.Element.Iterator.Element> {
// The default implementation of `_copyToNativeArrayBuffer` queries the
// `count` property, which materializes every inner collection. This is a
// bad default for `flatMap()`. So we treat `self` as a sequence and only
// rely on underestimated count.
return _copySequenceToNativeArrayBuffer(self)
}
internal var _base: Base
}
extension ${collectionForTraversal(traversal)}
where ${constraints % {'Base': ''}} {
/// A concatenation of the elements of `self`.
@warn_unused_result
public func flatten() -> ${Collection}<Self> {
return ${Collection}(self)
}
}
extension LazyCollectionProtocol
where
Self : ${collectionForTraversal(traversal)},
Elements : ${collectionForTraversal(traversal)},
${constraints % {'Base': ''}},
${constraints % {'Base': 'Elements.'}},
Iterator.Element == Elements.Iterator.Element {
/// A concatenation of the elements of `self`.
@warn_unused_result
public func flatten() -> LazyCollection<${Collection}<Elements>> {
return ${Collection}(elements).lazy
}
}
% end
@available(*, unavailable, renamed: "FlattenIterator")
public struct FlattenGenerator<
Base : IteratorProtocol where Base.Element : Sequence
> {}
extension FlattenSequence {
@available(*, unavailable, renamed: "iterator")
public func generate() -> FlattenIterator<Base.Iterator> {
fatalError("unavailable function can't be called")
}
}
% for traversal in ('Forward', 'Bidirectional'):
% t = '' if traversal == 'Forward' else traversal
% Collection = 'Flatten%sCollection' % t
extension ${Collection} {
@available(*, unavailable, message: "Please use underestimatedCount property instead.")
public func underestimateCount() -> Int {
fatalError("unavailable function can't be called")
}
}
%end