LazySequenceProtocol
A sequence on which normally-eager sequence operations are implemented lazily.
protocol LazySequenceProtocol : Sequence
Lazy sequences can be used to avoid needless storage allocation and computation, because they use an underlying sequence for storage and compute their elements on demand. For example, doubled
in this code sample is a sequence containing the values 2
, 4
, and 6
.
let doubled = [1, 2, 3].lazy.map { $0 * 2 }
Each time an element of the lazy sequence doubled
is accessed, the closure accesses and transforms an element of the underlying array.
Sequence operations that take closure arguments, such as map(_:)
and filter(_:)
, are normally eager: They use the closure immediately and return a new array. When you use the lazy
property, you give the standard library explicit permission to store the closure and the sequence in the result, and defer computation until it is needed.
Adding New Lazy Operations
To add a new lazy sequence operation, extend this protocol with a method that returns a lazy wrapper that itself conforms to LazySequenceProtocol
. For example, an eager scan(_:_:)
method is defined as follows:
extension Sequence {
/// Returns an array containing the results of
///
/// p.reduce(initial, nextPartialResult)
///
/// for each prefix `p` of `self`, in order from shortest to
/// longest. For example:
///
/// (1..<6).scan(0, +) // [0, 1, 3, 6, 10, 15]
///
/// - Complexity: O(n)
func scan<Result>(
_ initial: Result,
_ nextPartialResult: (Result, Element) -> Result
) -> [Result] {
var result = [initial]
for x in self {
result.append(nextPartialResult(result.last!, x))
}
return result
}
}
You can build a sequence type that lazily computes the elements in the result of a scan:
struct LazyScanSequence<Base: Sequence, Result>
: LazySequenceProtocol
{
let initial: Result
let base: Base
let nextPartialResult:
(Result, Base.Element) -> Result
struct Iterator: IteratorProtocol {
var base: Base.Iterator
var nextElement: Result?
let nextPartialResult:
(Result, Base.Element) -> Result
mutating func next() -> Result? {
return nextElement.map { result in
nextElement = base.next().map {
nextPartialResult(result, $0)
}
return result
}
}
}
func makeIterator() -> Iterator {
return Iterator(
base: base.makeIterator(),
nextElement: initial as Result?,
nextPartialResult: nextPartialResult)
}
}
Finally, you can give all lazy sequences a lazy scan(_:_:)
method:
extension LazySequenceProtocol {
func scan<Result>(
_ initial: Result,
_ nextPartialResult: @escaping (Result, Element) -> Result
) -> LazyScanSequence<Self, Result> {
return LazyScanSequence(
initial: initial, base: self, nextPartialResult: nextPartialResult)
}
}
With this type and extension method, you can call .lazy.scan(_:_:)
on any sequence to create a lazily computed scan. The resulting LazyScanSequence
is itself lazy, too, so further sequence operations also defer computation.
The explicit permission to implement operations lazily applies only in contexts where the sequence is statically known to conform to LazySequenceProtocol
. In the following example, because the extension applies only to Sequence
, side-effects such as the accumulation of result
are never unexpectedly dropped or deferred:
extension Sequence where Element == Int {
func sum() -> Int {
var result = 0
_ = self.map { result += $0 }
return result
}
}
Don’t actually use map
for this purpose, however, because it creates and discards the resulting array. Instead, use reduce
for summing operations, or forEach
or a for
-in
loop for operations with side effects.
Supertypes
protocol Sequence
A type that provides sequential, iterated access to its elements.
Requirements
associatedtype Elements : Sequence = Self
A
Sequence
that can contain the same elements as this one, possibly with a simpler type.var elements: Self.Elements
A sequence containing the same elements as this one, possibly with a simpler type.
Citizens in Swift
Members
var lazy: LazySequence<Self.Elements>
var lazy: Self.Elements
func compactMap<ElementOfResult>((Self.Elements.Element) -> ElementOfResult?
) -> LazyMapSequence<LazyFilterSequence<LazyMapSequence<Self.Elements, ElementOfResult?>>, ElementOfResult> Returns the non-
nil
results of mapping the given transformation over this sequence.func drop(while: (Self.Elements.Element) -> Bool
) -> LazyDropWhileSequence<Self.Elements> Returns a lazy sequence that skips any initial elements that satisfy
predicate
.func filter((Self.Elements.Element) -> Bool
) -> LazyFilterSequence<Self.Elements> Returns the elements of
self
that satisfyisIncluded
.func flatMap<SegmentOfResult>((Self.Elements.Element) -> SegmentOfResult
) -> LazySequence<FlattenSequence<LazyMapSequence<Self.Elements, SegmentOfResult>>> Returns the concatenated results of mapping the given transformation over this sequence.
func map<U>((Self.Element) -> U
) -> LazyMapSequence<Self.Elements, U> Returns a
LazyMapSequence
over thisSequence
. The elements of the result are computed lazily, each time they are read, by callingtransform
function on a base element.func prefix(while: (Self.Elements.Element) -> Bool
) -> LazyPrefixWhileSequence<Self.Elements> Returns a lazy sequence of the initial consecutive elements that satisfy
predicate
.func flatMap<ElementOfResult>((Self.Elements.Element) -> ElementOfResult?
) -> LazyMapSequence<LazyFilterSequence<LazyMapSequence<Self.Elements, ElementOfResult?>>, ElementOfResult> Returns the non-
nil
results of mapping the given transformation over this sequence.
Subtypes
Citizens in Swift
where Self.Element:Sequence
Members
func joined(
) -> LazySequence<FlattenSequence<Self.Elements>> Returns a lazy sequence that concatenates the elements of this sequence of sequences.
Extension in Algorithms
Members
func reductions((Self.Element, Self.Element) -> Self.Element
) -> InclusiveReductionsSequence<Self.Elements> Returns a sequence containing the accumulated results of combining the elements of the sequence using the given closure.
func reductions<Result>(Result, (Result, Self.Element) -> Result
) -> ExclusiveReductionsSequence<Self.Elements, Result> Returns a sequence containing the accumulated results of combining the elements of the sequence using the given closure.
func reductions<Result>(into: Result, (inout Result, Self.Element) -> Void
) -> ExclusiveReductionsSequence<Self.Elements, Result> Returns a sequence containing the accumulated results of combining the elements of the sequence using the given closure.
func split(maxSplits: Int, omittingEmptySubsequences: Bool, whereSeparator: (Self.Element) -> Bool
) -> SplitSequence<Self.Elements> Lazily returns the longest possible subsequences of the sequence, in order, that don’t contain elements satisfying the given predicate.
func uniqued<Subject>(on: (Self.Element) -> Subject
) -> UniquedSequence<Self, Subject> Returns a lazy sequence with the unique elements of this sequence (as determined by the given projection), in the order of the first occurrence of each unique element.
func scan((Self.Element, Self.Element) -> Self.Element
) -> InclusiveReductionsSequence<Self.Elements> func scan<Result>(Result, (Result, Self.Element) -> Result
) -> ExclusiveReductionsSequence<Self.Elements, Result> func scan<Result>(into: Result, (inout Result, Self.Element) -> Void
) -> ExclusiveReductionsSequence<Self.Elements, Result>
Extension in Algorithms
where Self:Collection, Self.Element:Equatable, Self.Elements:Collection
Members
func split(separator: Self.Element, maxSplits: Int, omittingEmptySubsequences: Bool
) -> SplitCollection<Self.Elements> Lazily returns the longest possible subsequences of the collection, in order, around elements equal to the given element.
Extension in Algorithms
where Self:Collection, Self.Elements:Collection
Members
func chunked(by: (Self.Element, Self.Element) -> Bool
) -> ChunkedByCollection<Self.Elements, Self.Element> Returns a lazy collection of subsequences of this collection, chunked by the given predicate.
func chunked<Subject>(on: (Self.Element) -> Subject
) -> ChunkedOnCollection<Self.Elements, Subject> Returns a lazy collection of subsequences of this collection, chunked by grouping elements that project to the same value.
func split(maxSplits: Int, omittingEmptySubsequences: Bool, whereSeparator: (Self.Element) -> Bool
) -> SplitCollection<Self.Elements> Lazily returns the longest possible subsequences of the collection, in order, that don’t contain elements satisfying the given predicate.
Extension in Algorithms
where Self.Element:Equatable
Members
func split(separator: Self.Element, maxSplits: Int, omittingEmptySubsequences: Bool
) -> SplitSequence<Self.Elements> Lazily returns the longest possible subsequences of the sequence, in order, around elements equal to the given element.
Extension in Algorithms
where Self.Element:Sequence
Members
func joined<Separator>(by: (Self.Element, Self.Element) -> Separator
) -> JoinedByClosureSequence<Self.Elements, Separator> Returns the concatenation of the elements in this sequence of sequences, inserting the separator produced by the closure between each sequence.
func joined(by: (Self.Element, Self.Element) -> Self.Element.Element
) -> JoinedByClosureSequence<Self.Elements, CollectionOfOne<Self.Element.Element>> Returns the concatenation of the elements in this sequence of sequences, inserting the separator produced by the closure between each sequence.
Extension in Algorithms
where Self.Element:Collection, Self.Elements:Collection
Members
func joined<Separator>(by: (Self.Element, Self.Element) -> Separator
) -> JoinedByClosureCollection<Self.Elements, Separator> Returns the concatenation of the elements in this collection of collections, inserting the separator produced by the closure between each sequence.
func joined(by: (Self.Element, Self.Element) -> Self.Element.Element
) -> JoinedByClosureCollection<Self.Elements, CollectionOfOne<Self.Element.Element>> Returns the concatenation of the elements in this collection of collections, inserting the separator produced by the closure between each sequence.