-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathLazySequence.swift
242 lines (226 loc) · 8.41 KB
/
LazySequence.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
//===--- LazySequence.swift -----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// A sequence on which normally-eager operations such as `map` and
/// `filter` are implemented lazily.
///
/// 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,
///
/// [1, 2, 3].lazy.map { $0 * 2 }
///
/// is a sequence containing { `2`, `4`, `6` }. Each time an element
/// of the lazy sequence is accessed, an element of the underlying
/// array is accessed and transformed by the closure.
///
/// Sequence operations taking closure arguments, such as `map` and
/// `filter`, are normally eager: they use the closure immediately and
/// return a new array. Using the `lazy` property gives the standard
/// library explicit permission to store the closure and the sequence
/// in the result, and defer computation until it is needed.
///
/// To add new lazy sequence operations, extend this protocol with
/// methods that return lazy wrappers that are themselves
/// `LazySequenceProtocol`s. For example, given an eager `scan`
/// method 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<ResultElement>(
/// _ initial: ResultElement,
/// _ nextPartialResult: (ResultElement, Element) -> ResultElement
/// ) -> [ResultElement] {
/// var result = [initial]
/// for x in self {
/// result.append(nextPartialResult(result.last!, x))
/// }
/// return result
/// }
/// }
///
/// we can build a sequence that lazily computes the elements in the
/// result of `scan`:
///
/// struct LazyScanIterator<Base : IteratorProtocol, ResultElement>
/// : IteratorProtocol {
/// mutating func next() -> ResultElement? {
/// return nextElement.map { result in
/// nextElement = base.next().map { nextPartialResult(result, $0) }
/// return result
/// }
/// }
/// private var nextElement: ResultElement? // The next result of next().
/// private var base: Base // The underlying iterator.
/// private let nextPartialResult: (ResultElement, Base.Element) -> ResultElement
/// }
///
/// struct LazyScanSequence<Base: Sequence, ResultElement>
/// : LazySequenceProtocol // Chained operations on self are lazy, too
/// {
/// func makeIterator() -> LazyScanIterator<Base.Iterator, ResultElement> {
/// return LazyScanIterator(
/// nextElement: initial, base: base.makeIterator(), nextPartialResult)
/// }
/// private let initial: ResultElement
/// private let base: Base
/// private let nextPartialResult:
/// (ResultElement, Base.Element) -> ResultElement
/// }
///
/// and finally, we can give all lazy sequences a lazy `scan` method:
///
/// extension LazySequenceProtocol {
/// /// Returns a sequence containing the results of
/// ///
/// /// p.reduce(initial, nextPartialResult)
/// ///
/// /// for each prefix `p` of `self`, in order from shortest to
/// /// longest. For example:
/// ///
/// /// Array((1..<6).lazy.scan(0, +)) // [0, 1, 3, 6, 10, 15]
/// ///
/// /// - Complexity: O(1)
/// func scan<ResultElement>(
/// _ initial: ResultElement,
/// _ nextPartialResult: (ResultElement, Element) -> ResultElement
/// ) -> LazyScanSequence<Self, ResultElement> {
/// return LazyScanSequence(
/// initial: initial, base: self, nextPartialResult)
/// }
/// }
///
/// - See also: `LazySequence`
///
/// - Note: The explicit permission to implement further operations
/// lazily applies only in contexts where the sequence is statically
/// known to conform to `LazySequenceProtocol`. Thus, side-effects such
/// as the accumulation of `result` below are never unexpectedly
/// dropped or deferred:
///
/// extension Sequence where Element == Int {
/// func sum() -> Int {
/// var result = 0
/// _ = self.map { result += $0 }
/// return result
/// }
/// }
///
/// [We don't recommend that you use `map` this way, because it
/// creates and discards an array. `sum` would be better implemented
/// using `reduce`].
public protocol LazySequenceProtocol : Sequence {
/// A `Sequence` that can contain the same elements as this one,
/// possibly with a simpler type.
///
/// - See also: `elements`
associatedtype Elements: Sequence = Self where Elements.Element == Element
/// A sequence containing the same elements as this one, possibly with
/// a simpler type.
///
/// When implementing lazy operations, wrapping `elements` instead
/// of `self` can prevent result types from growing an extra
/// `LazySequence` layer. For example,
///
/// _prext_ example needed
///
/// Note: this property need not be implemented by conforming types,
/// it has a default implementation in a protocol extension that
/// just returns `self`.
var elements: Elements { get }
}
/// When there's no special associated `Elements` type, the `elements`
/// property is provided.
extension LazySequenceProtocol where Elements == Self {
/// Identical to `self`.
@inlinable // protocol-only
public var elements: Self { return self }
}
extension LazySequenceProtocol {
@inlinable // protocol-only
public var lazy: LazySequence<Elements> {
return elements.lazy
}
}
extension LazySequenceProtocol where Elements: LazySequenceProtocol {
@inlinable // protocol-only
public var lazy: Elements {
return elements
}
}
/// A sequence containing the same elements as a `Base` sequence, but
/// on which some operations such as `map` and `filter` are
/// implemented lazily.
///
/// - See also: `LazySequenceProtocol`
@frozen // lazy-performance
public struct LazySequence<Base : Sequence> {
@usableFromInline
internal var _base: Base
/// Creates a sequence that has the same elements as `base`, but on
/// which some operations such as `map` and `filter` are implemented
/// lazily.
@inlinable // lazy-performance
internal init(_base: Base) {
self._base = _base
}
}
extension LazySequence: Sequence {
public typealias Element = Base.Element
public typealias Iterator = Base.Iterator
@inlinable
public __consuming func makeIterator() -> Iterator {
return _base.makeIterator()
}
@inlinable // lazy-performance
public var underestimatedCount: Int {
return _base.underestimatedCount
}
@inlinable // lazy-performance
@discardableResult
public __consuming func _copyContents(
initializing buf: UnsafeMutableBufferPointer<Element>
) -> (Iterator, UnsafeMutableBufferPointer<Element>.Index) {
return _base._copyContents(initializing: buf)
}
@inlinable // lazy-performance
public func _customContainsEquatableElement(_ element: Element) -> Bool? {
return _base._customContainsEquatableElement(element)
}
@inlinable // generic-performance
public __consuming func _copyToContiguousArray() -> ContiguousArray<Element> {
return _base._copyToContiguousArray()
}
}
extension LazySequence: LazySequenceProtocol {
public typealias Elements = Base
/// The `Base` (presumably non-lazy) sequence from which `self` was created.
@inlinable // lazy-performance
public var elements: Elements { return _base }
}
extension Sequence {
/// A sequence containing the same elements as this sequence,
/// but on which some operations, such as `map` and `filter`, are
/// implemented lazily.
@inlinable // protocol-only
public var lazy: LazySequence<Self> {
return LazySequence(_base: self)
}
}