forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLazySequence.swift
240 lines (224 loc) · 8.4 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
//===--- 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 sequence operations 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, `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.
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.
///
/// 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)
}
}