Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Cleanup Reverse tests, and refactor First/Last.#14174

Merged
stephentoub merged 7 commits intodotnet:masterfrom
jamesqo:reverse-partition
Dec 7, 2016
Merged

Cleanup Reverse tests, and refactor First/Last.#14174
stephentoub merged 7 commits intodotnet:masterfrom
jamesqo:reverse-partition

Conversation

@jamesqo
Copy link
Copy Markdown
Contributor

@jamesqo jamesqo commented Dec 3, 2016

Refactor First and FirstOrDefault and Last and LastOrDefault to share some code. Also, cleanup the tests for Reverse (previously, this PR contained changes related to that method).

cc @stephentoub @VSadov @JonHanna

Copy link
Copy Markdown
Contributor Author

@jamesqo jamesqo Dec 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried writing this as return found ? first : throw Error.NoElements() initially. But it looks like that feature of C# 7 isn't enabled yet 😢

@karelz karelz added this to the 1.2.0 milestone Dec 3, 2016
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the value of this change / extra code. This is simply to optimize the patterns:

source.Reverse().First();
source.Reverse().Last();

? Why wouldn't the author instead just write the simpler and faster:

source.Last();
source.First();

?

Copy link
Copy Markdown
Contributor Author

@jamesqo jamesqo Dec 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub The caller may not always have source cached in a local, so it may be more convenient to just get the first from Reverse() rather then reevaluating source. For example:

var items = cus.Where(c => c.Id == x).Reverse();

items.First(); // As opposed to calling cus.Last(c => c.Id == x)
items.Last(); // As opposed to calling cus.First(c => c.Id == x)

Another example is that source.Reverse() could have been passed to another method, where First or Last is called on it.

An additional reason for making these changes was that they were pretty trivial-- there are more red diffs than green in this PR in src/. And the benefits are pretty substantial; we are not just eliminating virtual method calls, we are also reducing allocations/big O complexity.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The caller may not always have source cached in a local

Then they could do ToArray or ToList. We should not be in the business of trying to optimize for every possible combination of operators, especially ones that are quite rare. Can you show me examples of real-world code where someone does a Reverse and then gets both the First and the Last element of it, and where it makes any difference to overall performance?

there are more red diffs than green in this PR

That's because you've combined two largely unrelated changes. You combined the First/FirstOrDefault implementations to avoid duplicating code, and then separately you added an IPartition implementation to Reverse.

Copy link
Copy Markdown
Contributor Author

@jamesqo jamesqo Dec 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then they could do ToArray or ToList.

OK, you have convinced me; I additionally just did a grep search through Roslyn's src/ and most of the usages are foreach or ToArray. I'll take this change out for now + reopen later if it turns out to be desirable. The refactoring + test changes will stay in this PR.

@jamesqo jamesqo changed the title Implement IPartition on Reverse, cleanup tests, and refactor First/Last. Cleanup Reverse tests, and refactor First/Last. Dec 3, 2016
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same consolidation can't be done for the overloads that take a predicate?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub Good idea. I've done this for the predicate overloads too. There are now more than 170 deleted lines 🎉

Comment thread src/System.Linq/tests/ReverseTests.cs Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you removing all of these?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub They do not seem to hit any extra codepaths. It's also unlikely we will specialize Reverse.Select or Reverse.Where since Reverse does not deal with selectors or predicates.

Comment thread src/System.Linq/tests/ReverseTests.cs Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you removing the nullable tests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub I can add them back.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, actually: They can't be fed into the [Theory] because xUnit cannot do type inference for nullables. (When an int? is boxed it's an int and xUnit will try to interpret the int?[] as an IEnumerable<int>.) I don't think there's a special case for nullables within Reverse.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I fixed this in xunit; maybe we need to update xunit?

Comment thread src/System.Linq/tests/ReverseTests.cs Outdated
Copy link
Copy Markdown
Member

@stephentoub stephentoub Dec 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why two separate selects rather than just:

.Select(c => new object[] { c.Select(i => i.ToString()), string.Empty })

?

Comment thread src/System.Linq/src/System/Linq/Last.cs Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied and pasted this from Last, which has a different local var name from LastOrDefault. That's why GitHub is showing diffs here.

Comment thread src/System.Linq/src/System/Linq/Last.cs Outdated
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here; I didn't actually write any of this logic, I copied it from Last.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran into a subtle difference between LastOrDefault and Last while making this refactoring. See the issue: https://github.com/dotnet/corefx/issues/14183 Not sure whether it should be >= or >. cc @JonHanna

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be >=, (while First uses < as they need to have opposite behaviour on tie-breaking to match the behaviour of doing a stable sort followed by getting the last or first item respectively).

@jamesqo
Copy link
Copy Markdown
Contributor Author

jamesqo commented Dec 4, 2016

I have some other optimizations in mind related to Where that also require First / FirstOrDefault to be refactored, so I will fold them into this PR shortly. (see https://github.com/dotnet/corefx/issues/13696 for more info)

@jamesqo jamesqo changed the title Cleanup Reverse tests, and refactor First/Last. [WIP] Cleanup Reverse tests, and refactor First/Last. Dec 4, 2016
@jamesqo jamesqo changed the title [WIP] Cleanup Reverse tests, and refactor First/Last. [WIP] Cleanup Reverse tests, refactor/optimize First/Last, optimize Where.First/Last. Dec 4, 2016
@jamesqo
Copy link
Copy Markdown
Contributor Author

jamesqo commented Dec 4, 2016

The aforementioned optimizations turned out to be a bit more complicated than I expected, so I may separate them into a new PR, actually.

@jamesqo jamesqo changed the title [WIP] Cleanup Reverse tests, refactor/optimize First/Last, optimize Where.First/Last. Cleanup Reverse tests, and refactor First/Last. Dec 4, 2016
@jamesqo
Copy link
Copy Markdown
Contributor Author

jamesqo commented Dec 4, 2016

@stephentoub I think this PR should be GTG (assuming you have no more comments).

Copy link
Copy Markdown
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @jamesqo.

@stephentoub stephentoub merged commit b29d8a7 into dotnet:master Dec 7, 2016
@jamesqo jamesqo deleted the reverse-partition branch December 7, 2016 22:21
steveharter pushed a commit to steveharter/dotnet_corefx that referenced this pull request Dec 19, 2016
* [WIP] Implement IPartition on Reverse.

* Finish.

* Cleanup Reverse tests

* Revert Reverse changes

* Refactor predicate overloads as well

* Fix regression.

* Respond to PR feedback
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* [WIP] Implement IPartition on Reverse.

* Finish.

* Cleanup Reverse tests

* Revert Reverse changes

* Refactor predicate overloads as well

* Fix regression.

* Respond to PR feedback


Commit migrated from dotnet/corefx@b29d8a7
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants