-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Override some collections methods in Range to use optimised implementations #7062
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
| if (contains(i)) (i - start) / step else -1 | ||
|
|
||
| override def sorted[B >: Int](implicit ord: Ordering[B]): IndexedSeq[Int] = | ||
| if (ord eq Ordering.Int) this else super.sorted(ord) |
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.
Is it worth also check if ord eq Ordering.Int.reverse to return this.reverse ?
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.
Maybe so. Anyone else have an opinion?
The main reason I did this is that Ordering.Int is the vastly most common value that this method will be passed, and it's also the one for which the least work can be done. Passing Int.Reverse is probably far rarer.
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.
Maybe Ordering.Int could also override val reverse: Ordering[Int] = ... so that it doesn't need to allocate a new instance each time
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 is actually completely wrong for negative step size
| case _ => super.indexOf(elem, from) | ||
| } | ||
|
|
||
| override final def lastIndexOf[@sp(Int) B >: Int](elem: B, end: Int = -1): Int = |
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.
the default for end should be length - 1
otherwise the result would always be -1 when not supplying an end index
as per SeqOps
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.
oops, good catch.
- that
| package scala | ||
| package collection.immutable | ||
|
|
||
| import scala.{specialized => sp} |
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.
is it usual/necessary to rename the import here?
This comment has been minimized.
This comment has been minimized.
|
maybe also def distinct as a noop unless step is 0 there are a bunch of iterator abstract methods as well that could be implemented. Not sure how often they are called though |
31efee1 to
c2d407e
Compare
|
@mkeskells |
This comment has been minimized.
This comment has been minimized.
| case 0 => other.isEmpty | ||
| case 1 => other.length == 1 && this.start == other.start | ||
| case n => other.length == n && ( | ||
| (this.start == other.start) |
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 about reverse ordered Range?
Range.inclusive(1,10,1) == Range.inclusive(10,1,-1)
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.
no, they're unequal; sameElements is confusingly named in that it means "same elements and in the same order"
|
@szeiger (and a little off topic) do we really need 2 implementation of Range (inclusive and exclusive) I dont think that the implementation classes, or isInclusive are part of the user API, so should be private[scala] at least. I presume this idea (if accepted) would require a deprecation cycle |
|
Some of these changes could be replicated/shared with NumericRange |
|
@mkeskells yes I'd like to share them; I just wanted review on the |
| } | ||
| } | ||
|
|
||
| override final def indexOf[@sp(Int) B >: Int](elem: B, from: Int = 0): Int = |
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.
porting this to NumericRange is nontrivial, because B is unbounded above; here we know the element type is Int (and can special-case it accordingly) but NumericRange has no way of telling whether elem is a T or not, so the Integral arithmetic is unsafe.
59ec32e to
a8e9cba
Compare
|
(rebased to maybe unwedge scabot) |
|
another method which may be profitably implemented here is |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
@mkeskells |
| * @param offset the index where the sequence is searched. | ||
| * @return `true` if the sequence `that` is contained in this $coll at | ||
| * index `offset`, otherwise `false`. | ||
| * @throws NoSuchElementException if `offset` is greater than the size of this $coll |
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.
No, it should never throw an exception. See scala/bug#10931 and scala/bug#11199 for semantics in corner cases.
| if (this.isEmpty && other.isEmpty) true | ||
| else { | ||
| (this.length : @annotation.switch) match { | ||
| case 0 => other.isEmpty |
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.
You already know that other.isEmpty is false at this point
|
@szeiger I think that is what I was proposing - keeping the APIs but having one implementation |
|
Ping @hrhino |
a8e9cba to
6d52192
Compare
|
@hrhino temporarily taking over pinging duties. Curious if there were any other contenders for oldest mergeable PR. |
6d52192 to
a966258
Compare
| case n => other.length == n && ( | ||
| (this.start == other.start) | ||
| && (this.step == other.step) | ||
| && (this.last == other.last) |
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.
Comparing both length and last appears to be redundant
| case _ => super.lastIndexOf(elem, end) | ||
| } | ||
|
|
||
| override def indexOfSlice[B >: Char](that: collection.Seq[B], from: Int = 0): Int = |
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.
Nice catch. We should put import scala.Predef.{wrapString => _, _} into this file to avoid such accidents. ArrayOps does the same for the array conversions.
|
@hrhino interested in returning to this? time is running short for RC1 |
|
the remaining review feedback was minor. I added two commits in response if CI likes it, let's squash, rebase, and merge. |
…ations. - `indexOf` and `lastIndexOf` are calculable in constant time - `sameElements` can be calculated by comparing `start`/`step`/`last`, if the other collection is also a `Range` - `contains` can't override `Seq`'s `contains`, but we can add a forwarder that will
I don't know why I thought it was on jl.String.
0cf8c11 to
ea470f7
Compare
|
squashed, rebased |
|
@SethTisue yes, this has gotten older than I would have thought! What more do you need from me? |
|
nothing, we're good. thanks 👍 |
|
My previous comment hinting that there is a competition for the title of oldest active PR was not intended to elicit protestations of false modesty. hrhino's offer of further help is a transparent ploy to keep it open a few more weeks and extend its standing. More experienced contributors know that the most effective tactic is to set the milestone a few point releases down the road; in a previous era, one had only to request comment from soc to guarantee that a PR remain unmerged for months or even years. |
Changes from: * scala/scala#7784 * scala/scala#7062
indexOfandlastIndexOfare calculable in constant timesortedon aRange, if it uses the defaultIntordering, is a no-opsameElementscan be calculated by comparingstart/step/last, if theother collection is also a
Rangecc @mkeskells