Skip to content
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

make last work on reversible any collection #42991

Merged
merged 13 commits into from
Nov 10, 2021

Conversation

MasonProtter
Copy link
Contributor

@MasonProtter MasonProtter commented Nov 7, 2021

As suggested by @simeonschaub in #42943 (comment), this changes the default fallback of last(x) from x[end] to first(Iterators.reverse(x)), which allows it to work on any collection that supports being reversed, rather than just collections that have indexing.

Before:

julia> last((x for x in 1:3))
ERROR: MethodError: no method matching lastindex(::Base.Generator{UnitRange{Int64}, typeof(identity)}

After:

julia> last((x for x in 1:3))
3

julia> last((x for x in 1:3 if iseven(x)))
2

Fixes #42943

@ararslan ararslan added the needs compat annotation Add !!! compat "Julia x.y" to the docstring label Nov 7, 2021
Copy link
Member

@simeonschaub simeonschaub left a comment

Choose a reason for hiding this comment

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

Will need to delete

first(r::Reverse) = last(r.itr) # and the last shall be first

@MasonProtter
Copy link
Contributor Author

Ah of course. What would be an appropriate replacement for this? I suppose we could use

first(r::Reverse) = foldl((_, x) -> x, r.itr)

I think if itr supports reverse then folding over it should be fair game, but maybe not. Or should it just do something like iterate(r)[1]?

@simeonschaub
Copy link
Member

I think that line can just be deleted, so it falls back to the generic first implementation, i.e. basically the latter. Should probably double-check though that this doesn't cause any regressions.

@MasonProtter
Copy link
Contributor Author

Okay, let's see what happens

@simeonschaub
Copy link
Member

Ah, I guess you might have to keep the old version for AbstractVector for bootstrapping purposes.

@MasonProtter
Copy link
Contributor Author

Any reason to not keep it for AbstractArray generally?

@simeonschaub
Copy link
Member

That's probably fine, although I don't think we generally assume all AbstractArrays support linear indexing.

Copy link
Member

@simeonschaub simeonschaub left a comment

Choose a reason for hiding this comment

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

LGTM

@simeonschaub simeonschaub added iteration Involves iteration or the iteration protocol and removed needs compat annotation Add !!! compat "Julia x.y" to the docstring labels Nov 8, 2021
base/abstractarray.jl Outdated Show resolved Hide resolved
Co-authored-by: Simeon Schaub <[email protected]>
@vtjnash vtjnash merged commit 1f484c3 into JuliaLang:master Nov 10, 2021
@@ -103,7 +103,6 @@ size(r::Reverse) = size(r.itr)
IteratorSize(::Type{Reverse{T}}) where {T} = IteratorSize(T)
IteratorEltype(::Type{Reverse{T}}) where {T} = IteratorEltype(T)
last(r::Reverse) = first(r.itr) # the first shall be last
first(r::Reverse) = last(r.itr) # and the last shall be first
Copy link
Member

Choose a reason for hiding this comment

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

We might need to put this back, with a special attempt not to form an infinite recursion, but let's wait for PkgEval to determine that.

@bkamins
Copy link
Member

bkamins commented Nov 11, 2021

@MasonProtter This PR has broken DataFrames.jl tests.

I assume the reason is it indirectly introduced triggering of the following:

julia> iterate(Iterators.reverse((a=1, b=2)))
ERROR: MethodError: no method matching iterate(::Base.Iterators.Reverse{NamedTuple{(:a, :b), Tuple{Int64, Int64}}})

I have not looked at the issue in detail but probably adding:

iterate(r::Reverse{<:NamedTuple}, i::Int = length(r.itr)) = i < 1 ? nothing : (r.itr[i], i-1)

should be enough.

bkamins added a commit to bkamins/julia that referenced this pull request Nov 11, 2021
@JeffBezanson
Copy link
Member

This could definitely break a lot of things, since getindex methods are much more common than methods for Iterators.Reverse, which is slightly obscure.

@@ -476,7 +480,8 @@ julia> last([1; 2; 3; 4])
4
```
"""
last(a) = a[end]
last(a) = first(Iterators.reverse(a))
last(a::AbstractVector) = a[end]
Copy link
Member

Choose a reason for hiding this comment

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

I think this can be for AbstractArray.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did that above, but @simeonschaub mentioned that since there are abstract arrays that don't support linear indexing, this might be the better default. Can give AbstractArray a try though and see how it goes

Copy link
Member

Choose a reason for hiding this comment

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

IIRC, all arrays support linear indexing, but it just isn't required to be the most efficient index

Copy link
Member

Choose a reason for hiding this comment

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

An array can also have a lastindex that isn't necessarily an integer.

Copy link
Member

Choose a reason for hiding this comment

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

I thought so too, but lastindex is actually defined to always return an integer.

@MasonProtter
Copy link
Contributor Author

This could definitely break a lot of things, since getindex methods are much more common than methods for Iterators.Reverse, which is slightly obscure.

In that case, would it make sense to have an interate(::Reverse) fallback method that uses indexing?

simeonschaub pushed a commit that referenced this pull request Nov 13, 2021
vtjnash added a commit that referenced this pull request Nov 16, 2021
This reverts commit 1f484c3 since it
seems to cause excess breakage for the benefits it brings.
vtjnash added a commit that referenced this pull request Nov 16, 2021
vtjnash added a commit that referenced this pull request Nov 18, 2021
Revert "make last work on any reversible collection (#42991), and fix it
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Feb 22, 2022
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Feb 22, 2022
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Feb 22, 2022
This reverts commit 1f484c3 since it
seems to cause excess breakage for the benefits it brings.
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Feb 22, 2022
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Mar 8, 2022
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Mar 8, 2022
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Mar 8, 2022
This reverts commit 1f484c3 since it
seems to cause excess breakage for the benefits it brings.
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Mar 8, 2022
@MasonProtter MasonProtter deleted the patch-3 branch October 30, 2024 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
iteration Involves iteration or the iteration protocol
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Define last for non-O(1) types?
6 participants