-
Notifications
You must be signed in to change notification settings - Fork 442
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
Add BidirectionalCollection.trimming #4
Conversation
In terms of code style, I think my defaults match this project (except that I use a 120 line length). Does this project just use the default swift-format settings? |
Sources/Algorithms/Trim.swift
Outdated
var sliceStart = startIndex | ||
var sliceEnd = endIndex | ||
// Consume elements from the front. | ||
while sliceStart != sliceEnd, try predicate(self[sliceStart]) { |
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.
Can you use existing stdlib methods here, instead of searching for the start and end manually?
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 can use firstIndex(where:)
to find the start, but not lastIndex(where:)
to find the end (since we actually want the index after the last index to match the predicate; this would mean an extra call to index(after:)
).
I've tried to improve the documentation, too. It might be worth adding a String.trimmed()
function which defaults to trimming whitespace.
60fcfb9
to
3993caf
Compare
Thanks for this, @karwa! 🎉 We do need a guide for this addition — the symbol docs describe the usage and semantics, while the guide can cover more about the intention, design, what choices were considered but not taken, and how this feature relates to other similar ones in other languages. A survey of the naming in other languages would be interesting to see — I think I've seen this called I've also been thinking a bit about how this relates to what we already have in the standard library. The matching existing method is All that makes me think that "trim" may be a better root verb for all three operations — maybe |
@natecook1000 I've added a guide document. It may be a little verbose, I don't know 😅. That's just my style - it's how I write, but I did my best. I agree with |
@natecook1000 is there anything remaining for me to do 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.
Looking good! Just a couple notes about the documentation.
Guides/Trim.md
Outdated
A less-efficient implementation is _possible_ for any `Collection`, which would involve always traversing the | ||
entire collection. This implementation is not provided, as it would mean developers of generic algorithms who forget | ||
to add the `BidirectionalCollection` constraint will recieve that inefficient implementation: |
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.
Agreed, BidirectionalCollection
is the right place for this.
Sources/Algorithms/Trim.swift
Outdated
/// print(myString.trimmed(where: \.isWhitespace)) // "hello, world" | ||
/// ``` | ||
/// | ||
/// [1]: https://en.wikipedia.org/wiki/Trimming_(computer_programming) |
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.
/// [1]: https://en.wikipedia.org/wiki/Trimming_(computer_programming) |
We should mention that if the entire collection is trimmed, the result is an empty subsequence at the end of the collection.
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.
Personally I'd prefer to leave the position of the empty slice unspecified. If we decided we wanted to change this to first trim elements from the tail and then the head, the result when trimming the entire collection would be an empty subsequence at startIndex
.
while sliceStart != sliceEnd { | ||
let idxBeforeSliceEnd = index(before: sliceEnd) | ||
guard try predicate(self[idxBeforeSliceEnd]) else { | ||
return self[sliceStart..<sliceEnd] |
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.
Q: can these bounds be safely unchecked too?
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'm not 100% sure. Since sliceEnd
is formed by walking backwards from endIndex
and has not yet tested equal to sliceStart
, the only way this bounds check will fail is if either your collection or the index's Comparable
implementation is broken.
For the empty result, we already checked that sliceStart == sliceEnd
as part of the while
loop condition, so it is safe to omit the bounds check. A broken Equatable
implementation would never get to that part.
Guides/Trim.md
Outdated
which satisfy the given predicate. | ||
|
||
```swift | ||
let results = [2, 10, 11, 15, 20, 21, 100].trimmed(where: { $0.isMultiple(of: 2) }) |
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.
Just a teeny bit of bikeshedding:
Because trailing closure syntax causes where:
to be omitted, there can be a little bit of ambiguity as to whether the closure determines what's included or what's excluded. It so happens that, in English, "trimming" can take a direct object, while "trimmed" cannot, such that [1, 2, 3].trimming { $0 == 1 }
suggests that every element equal to one is being dropped a little more strongly than "trimmed" would:
"123abc", trim numerals → "abc"
"123abc", trimming numerals → "abc"
"123abc", trimmed numerals → "123" or "abc"?
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.
Yeah, that sounds good. I like trimming
.
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.
A remark on documentation.
Apologies for the delay. I've addressed your comments @xwu and @natecook1000 - documentation has been improved, and I think Xiaodi makes a good case for the name
|
Description
Adds a '.trimming' method to BidirectionalCollection, returning a slice with matching elements removed from the start and end.
Detailed Design
Documentation Plan
No existing guides need updating AFAIK. I haven't written a guide for this feature: is it necessary for every API to include this parallel documentation?
Test Plan
Tests are included.
Source Impact
Additive.
Checklist