-
Notifications
You must be signed in to change notification settings - Fork 359
Add Heap.removeAll(where:) #454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
lorentey
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks; adding this is a good idea!
The implementation needs a bit of improvement.
Note that this project uses Swift stdlib indentation rules: our basic unit of indentation is two spaces (not four, not three). The unit of indentation must be kept consistent in the entire project.
Sources/HeapModule/Heap.swift
Outdated
|
|
||
| /// Removes all the elements that satisfy the given predicate. | ||
| /// | ||
| /// The heap *must not* be empty. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe I made this decision based off of the methods like replaceMax, however, now looking at other removeAll methods this is never a precondition so I believe the right decision is to not have this precondition.
Sources/HeapModule/Heap.swift
Outdated
| ) rethrows { | ||
| // Precondition: the heap must not be empty. | ||
| precondition(!isEmpty, "Heap must not be empty") | ||
| _storage = try _storage.filter { try !shouldBeRemoved($0) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method needs to forward to the standard MutableCollection/RangeReplaceableCollection method that implements the same.
| _storage = try _storage.filter { try !shouldBeRemoved($0) } | |
| _storage = try _storage.removeAll(where: shouldBeRemoved) |
Sources/HeapModule/Heap.swift
Outdated
| public mutating func removeAll( | ||
| where shouldBeRemoved: (Element) throws -> Bool | ||
| ) rethrows { | ||
| // Precondition: the heap must not be empty. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you say is the purpose of this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems quite redundant now that its pointed it out haha
Sources/HeapModule/Heap.swift
Outdated
| where shouldBeRemoved: (Element) throws -> Bool | ||
| ) rethrows { | ||
| // Precondition: the heap must not be empty. | ||
| precondition(!isEmpty, "Heap must not be empty") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for adding this precondition?
Sources/HeapModule/Heap.swift
Outdated
| _update { handle in | ||
| handle.heapify() | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happened to the indentation here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about that, will go back and fix style.
| handle.heapify() | ||
| } | ||
| } | ||
| _checkInvariants() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If invariant checking is enabled, then it needs to be done on every exit path. The way to do that is by nesting this call in a defer block, and putting it on the top of the function, like the other operations do.
Tests/HeapTests/HeapTests.swift
Outdated
| } | ||
|
|
||
| func test_removeAll_noneRemoved() { | ||
| var heap = Heap<Int>((1...10).shuffled()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two issues:
- Our tests need to have repeatable results; truly random shuffling is not acceptable.
- (minor) While there is nothing wrong with only looking at a single count, it's almost trivial to try a range of counts, so let's just do that!
The other test cases show how to produce reproducibly random input for such purposes, and how to try many different counts.
Tests/HeapTests/HeapTests.swift
Outdated
|
|
||
| func test_removeAll_noneRemoved() { | ||
| var heap = Heap<Int>((1...10).shuffled()) | ||
| let originalSorted = heap.unordered.sorted() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It pays to be paranoid with tests -- the expected results must not be tainted by going through the same construct that the test is verifying. We know that the heap holds the elements 1 ... 10; we don't need to ask the heap to feed them back to us, and then sort the result ourselves.
let expected = 1 ... 10
heap.removeAll { _ in false }
expectEqualElements(heap.itemsInAscendingOrder(), expected)
|
@swift-ci test |
This PR introduces a new method
Heap.removeAll(where:)that allows users to remove all elements satisfying a given predicate from the heap in O(n) time. This is done by filtering the buffer and re-heapifying.Closes #255
Checklist