Skip to content

Commit ad8d5a9

Browse files
natecook1000airspeedswift
authored andcommitted
[stdlib] Stop precounting lazily filtered collections (swiftlang#8038)
This eliminates the counting step for a lazy filtered collection when converting it into an array by treating the collection as a sequence when copying elements. FlattenCollections already have this behavior. (SR-4164)
1 parent 7863e29 commit ad8d5a9

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed

stdlib/public/core/Filter.swift.gyb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,22 @@ public struct ${Self}<
212212
return ${Slice}(base: self, bounds: bounds)
213213
}
214214

215+
// Any estimate of the number of elements that pass `_predicate` requires
216+
// iterating the collection and evaluating each element, which can be costly,
217+
// is unexpected, and usually doesn't pay for itself in saving time through
218+
// preventing intermediate reallocations. (SR-4164)
219+
public var underestimatedCount: Int { return 0 }
220+
221+
public func _copyToContiguousArray()
222+
-> ContiguousArray<Base.Iterator.Element> {
223+
224+
// The default implementation of `_copyToContiguousArray` queries the
225+
// `count` property, which evaluates `_predicate` for every element --
226+
// see the note above `underestimatedCount`. Here we treat `self` as a
227+
// sequence and only rely on underestimated count.
228+
return _copySequenceToContiguousArray(self)
229+
}
230+
215231
// FIXME(ABI)#28 (Associated Types with where clauses): we actually want to add:
216232
//
217233
// typealias SubSequence = ${Self}<Base.SubSequence>

test/stdlib/Filter.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,23 @@ FilterTests.test("filtering sequences") {
5454
expectEqualSequence([7, 14, 21, 28], f1)
5555
}
5656

57+
FilterTests.test("single-count") {
58+
// Check that we're only calling a lazy filter's predicate one time for
59+
// each element in a sequence or collection.
60+
var count = 0
61+
let mod7AndCount: (Int) -> Bool = {
62+
count += 1
63+
return $0 % 7 == 0
64+
}
65+
66+
let f0 = (0..<30).makeIterator().lazy.filter(mod7AndCount)
67+
let a0 = Array(f0)
68+
expectEqual(30, count)
69+
70+
count = 0
71+
let f1 = LazyFilterCollection(_base: 0..<30, mod7AndCount)
72+
let a1 = Array(f1)
73+
expectEqual(30, count)
74+
}
75+
5776
runAllTests()

0 commit comments

Comments
 (0)