mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
stdlib: add a hook for dynamic dispatch in CollectionType.find()
This hook allows Set.find() to be equally efficient in static and generic contexts. This time, with correct tests. Swift SVN r27404
This commit is contained in:
@@ -77,6 +77,22 @@ extension _CollectionDefaultsType {
|
||||
final public func _prext_underestimateCount() -> Int {
|
||||
return numericCast(count(self))
|
||||
}
|
||||
|
||||
/// Customization point for `SequenceType._prext_find()`.
|
||||
///
|
||||
/// Define this method if the collection can find an element in less than
|
||||
/// O(N) by exploiting collection-specific knowledge.
|
||||
///
|
||||
/// Returns: `nil` if a linear search should be attempted instead,
|
||||
/// `Optional(nil)` if the element was not found, or
|
||||
/// `Optional(Optional(index))` if an element was found.
|
||||
///
|
||||
/// Complexity: O(N)
|
||||
final public func _customFindEquatableElement(
|
||||
element: Generator.Element
|
||||
) -> Index?? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// A multi-pass *sequence* with addressable positions.
|
||||
@@ -102,6 +118,18 @@ public protocol CollectionType
|
||||
/// `position != endIndex`.
|
||||
subscript(position: Index) -> Generator.Element {get}
|
||||
|
||||
/// Customization point for `SequenceType._prext_find()`.
|
||||
///
|
||||
/// Define this method if the collection can find an element in less than
|
||||
/// O(N) by exploiting collection-specific knowledge.
|
||||
///
|
||||
/// Returns: `nil` if a linear search should be attempted instead,
|
||||
/// `Optional(nil)` if the element was not found, or
|
||||
/// `Optional(Optional(index))` if an element was found.
|
||||
///
|
||||
/// Complexity: O(N)
|
||||
func _customFindEquatableElement(element: Generator.Element) -> Index??
|
||||
|
||||
// Do not use this operator directly; call `count(x)` instead
|
||||
func ~> (_:Self, _:(_Count, ())) -> Index.Distance
|
||||
}
|
||||
|
||||
@@ -49,9 +49,11 @@ extension CollectionType where Self.${GElement} : Equatable {
|
||||
///
|
||||
/// Complexity: O(\ `self.count()`\ )
|
||||
final public func _prext_find(element: ${GElement}) -> Index? {
|
||||
// FIXME: dynamic dispatch for Set and Dictionary.
|
||||
// FIXME: _prext_indices
|
||||
for i in indices(self) {
|
||||
if let result? = _customFindEquatableElement(element) {
|
||||
return result
|
||||
}
|
||||
|
||||
for i in self._prext_indices {
|
||||
if self[i] == element {
|
||||
return i
|
||||
}
|
||||
|
||||
@@ -640,6 +640,10 @@ public struct Set<T : Hashable> :
|
||||
public func _customContainsEquatableElement(member: T) -> Bool? {
|
||||
return contains(member)
|
||||
}
|
||||
|
||||
public func _customFindEquatableElement(member: T) -> Index?? {
|
||||
return Optional(indexOf(member))
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for both subset and equality relationship between
|
||||
|
||||
@@ -2292,6 +2292,132 @@ SequenceTypeAlgorithms.test("find/WhereElementIsEquatable/${dispatch}") {
|
||||
|
||||
% end
|
||||
|
||||
struct CollectionWithCustomFindMethod : CollectionType {
|
||||
static var timesFindWasCalled: Int = 0
|
||||
|
||||
internal let _elements: [Int]
|
||||
|
||||
init(_ elements: [Int]) {
|
||||
self._elements = elements
|
||||
}
|
||||
|
||||
func generate() -> MinimalGenerator<MinimalEquatableValue> {
|
||||
// Lie from our generate() method about sequence contents.
|
||||
// Tests using this type should not call generate() anyway.
|
||||
expectUnreachable()
|
||||
return MinimalSequence<MinimalEquatableValue>([]).generate()
|
||||
}
|
||||
|
||||
var startIndex: MinimalForwardIndex {
|
||||
return MinimalForwardIndex(
|
||||
position: 0, startIndex: 0, endIndex: _elements.endIndex)
|
||||
}
|
||||
|
||||
var endIndex: MinimalForwardIndex {
|
||||
return MinimalForwardIndex(
|
||||
position: _elements.endIndex,
|
||||
startIndex: 0,
|
||||
endIndex: _elements.endIndex)
|
||||
}
|
||||
|
||||
subscript(i: MinimalForwardIndex) -> MinimalEquatableValue {
|
||||
return MinimalEquatableValue(_elements[i.position])
|
||||
}
|
||||
|
||||
func _customFindEquatableElement(
|
||||
element: MinimalEquatableValue
|
||||
) -> MinimalForwardIndex?? {
|
||||
++CollectionWithCustomFindMethod.timesFindWasCalled
|
||||
for i in _elements._prext_indices {
|
||||
if _elements[i] == element.value {
|
||||
return MinimalForwardIndex(
|
||||
position: i,
|
||||
startIndex: 0,
|
||||
endIndex: _elements.endIndex)
|
||||
}
|
||||
}
|
||||
return Optional(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func callStaticFind(
|
||||
sequence: CollectionWithCustomFindMethod,
|
||||
element: MinimalEquatableValue
|
||||
) -> CollectionWithCustomFindMethod.Index? {
|
||||
return sequence._prext_find(element)
|
||||
}
|
||||
|
||||
% for dispatch in [ 'Static', 'Generic' ]:
|
||||
|
||||
SequenceTypeAlgorithms.test("find/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
|
||||
for test in findTests {
|
||||
let s = CollectionWithCustomFindMethod(test.sequence)
|
||||
CollectionWithCustomFindMethod.timesFindWasCalled = 0
|
||||
expectEqual(
|
||||
test.expected,
|
||||
call${dispatch}Find(s, MinimalEquatableValue(test.element))
|
||||
.map { $0.position },
|
||||
stackTrace: test.loc.withCurrentLoc())
|
||||
expectEqual(1, CollectionWithCustomFindMethod.timesFindWasCalled)
|
||||
}
|
||||
}
|
||||
|
||||
% end
|
||||
|
||||
// FIXME: underscores are a workaround for:
|
||||
// <rdar://problem/20582358> Commenting out one line determines whether a
|
||||
// completely different line type-checks
|
||||
func callGenericFind_<
|
||||
C : CollectionType where C.Generator.Element : Equatable
|
||||
>(collection: C, element: C.Generator.Element) -> C.Index? {
|
||||
return collection._prext_find(element)
|
||||
}
|
||||
|
||||
func callStaticFind_(
|
||||
set: Set<MinimalHashableValue>,
|
||||
element: MinimalHashableValue
|
||||
) -> Set<MinimalHashableValue>.Index? {
|
||||
return set._prext_find(element)
|
||||
}
|
||||
|
||||
% for dispatch in [ 'Static', 'Generic' ]:
|
||||
|
||||
// FIXME: implement the same optimization for Dictionary.
|
||||
// FIXME: move to the file where other Set tests live.
|
||||
SequenceTypeAlgorithms.test("Set<T>.find/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
|
||||
for test in findTests {
|
||||
let s = Set<MinimalHashableValue>(
|
||||
test.sequence.map { MinimalHashableValue($0) })
|
||||
MinimalHashableValue.timesEqualEqualWasCalled = 0
|
||||
MinimalHashableValue.timesHashValueWasCalled = 0
|
||||
expectEqual(
|
||||
test.expected
|
||||
.map { _ in MinimalHashableValue(test.element) },
|
||||
call${dispatch}Find_(s, MinimalHashableValue(test.element))
|
||||
.map { s[$0] },
|
||||
stackTrace: test.loc.withCurrentLoc())
|
||||
if test.sequence.isEmpty {
|
||||
expectEqual(
|
||||
0, MinimalHashableValue.timesEqualEqualWasCalled,
|
||||
stackTrace: test.loc.withCurrentLoc())
|
||||
expectEqual(
|
||||
0, MinimalHashableValue.timesHashValueWasCalled,
|
||||
stackTrace: test.loc.withCurrentLoc())
|
||||
} else {
|
||||
expectNotEqual(
|
||||
0, MinimalHashableValue.timesHashValueWasCalled,
|
||||
stackTrace: test.loc.withCurrentLoc())
|
||||
}
|
||||
if test.expected != nil {
|
||||
expectNotEqual(
|
||||
0, MinimalHashableValue.timesEqualEqualWasCalled,
|
||||
stackTrace: test.loc.withCurrentLoc())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
% end
|
||||
|
||||
SequenceTypeAlgorithms.test("find/Predicate") {
|
||||
for test in findTests {
|
||||
let s = MinimalForwardCollection<MinimalEquatableValue>(
|
||||
|
||||
@@ -3226,6 +3226,14 @@ SetTestSuite.test("first") {
|
||||
expectEmpty(emptySet.first)
|
||||
}
|
||||
|
||||
SetTestSuite.test("isEmpty") {
|
||||
let s1 = Set([1010, 2020, 3030])
|
||||
expectFalse(s1.isEmpty)
|
||||
|
||||
let emptySet = Set<Int>()
|
||||
expectTrue(emptySet.isEmpty)
|
||||
}
|
||||
|
||||
SetTestSuite.test("count") {
|
||||
let s1 = Set([1010, 2020, 3030])
|
||||
var s2 = Set([4040, 5050, 6060])
|
||||
@@ -3240,6 +3248,29 @@ SetTestSuite.test("contains") {
|
||||
expectFalse(Set<Int>().contains(1010))
|
||||
}
|
||||
|
||||
SetTestSuite.test("_customContainsEquatableElement") {
|
||||
let s1 = Set([1010, 2020, 3030, 4040, 5050, 6060])
|
||||
expectTrue(s1._customContainsEquatableElement(1010)!)
|
||||
expectFalse(s1._customContainsEquatableElement(999)!)
|
||||
expectFalse(Set<Int>()._customContainsEquatableElement(1010)!)
|
||||
}
|
||||
|
||||
SetTestSuite.test("indexOf") {
|
||||
let s1 = Set([1010, 2020, 3030, 4040, 5050, 6060])
|
||||
let foundIndex1 = s1.indexOf(1010)!
|
||||
expectEqual(1010, s1[foundIndex1])
|
||||
|
||||
expectEmpty(s1.indexOf(999))
|
||||
}
|
||||
|
||||
SetTestSuite.test("_customFindEquatableElement") {
|
||||
let s1 = Set([1010, 2020, 3030, 4040, 5050, 6060])
|
||||
let foundIndex1 = s1._customFindEquatableElement(1010)!!
|
||||
expectEqual(1010, s1[foundIndex1])
|
||||
|
||||
expectEmpty(s1._customFindEquatableElement(999)!)
|
||||
}
|
||||
|
||||
SetTestSuite.test("commutative") {
|
||||
let s1 = Set([1010, 2020, 3030])
|
||||
let s2 = Set([2020, 3030])
|
||||
|
||||
Reference in New Issue
Block a user