-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
adds the nth
function for iterables
#56580
base: master
Are you sure you want to change the base?
Changes from 3 commits
6a0b4bd
1de4a8f
11ee27a
3870e3e
8bdd4a0
7326e87
58b60e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ import .Base: | |
getindex, setindex!, get, iterate, | ||
popfirst!, isdone, peek, intersect | ||
|
||
export enumerate, zip, rest, countfrom, take, drop, takewhile, dropwhile, cycle, repeated, product, flatten, flatmap, partition | ||
export enumerate, zip, rest, countfrom, take, drop, takewhile, dropwhile, cycle, repeated, product, flatten, flatmap, partition, nth | ||
public accumulate, filter, map, peel, reverse, Stateful | ||
|
||
""" | ||
|
@@ -1595,4 +1595,72 @@ end | |
# be the same as the keys, so this is a valid optimization (see #51631) | ||
pairs(s::AbstractString) = IterableStatePairs(s) | ||
|
||
""" | ||
nth(itr, n::Integer) | ||
|
||
Get the `n`th element of an iterable collection. Return `nothing` if not existing. | ||
|
||
ghyatzo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
See also: [`first`](@ref), [`last`](@ref) | ||
|
||
# Examples | ||
```jldoctest | ||
julia> nth(2:2:10, 4) | ||
8 | ||
|
||
julia> nth(reshape(1:30, (5,6)), 6) | ||
6 | ||
``` | ||
""" | ||
nth(itr, n::Integer) = _nth(IteratorSize(itr), itr, n) | ||
nth(itr::AbstractArray, n::Integer) = n > length(itr) ? nothing : itr[n] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assumes one-based indexing. Perhaps do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you are absolutely correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went with the probably overkill approach, if it's too much i'll revert back to your suggestion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I could gather that is included in the unsafe_getindex(v::AbstractRange{T}, i::Integer) where T = convert(T, first(v) + (i - oneunit(i))*step_hp(v)) which should pretty much be the same sa There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was overthinking it, I'll just stick with |
||
|
||
_nth(::SizeUnknown, itr, n) = _fallback_nth(itr, n) | ||
_nth(::Union{HasShape,HasLength}, itr, n) = _withlength_nth(itr, n, length(itr)) | ||
_nth(::IsInfinite, itr, n) = _inbounds_nth(itr, n) | ||
|
||
_inbounds_nth(itr, n) = iterate(drop(itr, n - 1))[1] | ||
_inbounds_nth(itr::AbstractArray, n) = itr[n] | ||
ghyatzo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
_withlength_nth(itr, n, N) = n > N ? nothing : _inbounds_nth(itr, n) | ||
|
||
function _fallback_nth(itr, n) | ||
y = iterate(drop(itr, n - 1)) | ||
y === nothing && return nothing | ||
y[1] | ||
end | ||
|
||
# specialized versions to better interact with existing iterators | ||
# Count | ||
nth(itr::Count, n::Integer) = n > 0 ? itr.start + itr.step * (n - 1) : nothing | ||
|
||
# Repeated | ||
nth(itr::Repeated, n::Integer) = itr.x | ||
|
||
# Take(Repeated) | ||
nth(itr::Take{Repeated{O}}, n::Integer) where {O} = n > itr.n ? nothing : itr.xs.x | ||
|
||
# infinite cycle | ||
nth(itr::Cycle{I}, n::Integer) where {I} = _nth_inf_cycle(IteratorSize(I), itr, n) | ||
_nth_inf_cycle(::IsInfinite, itr, n) = _inbounds_nth(itr.xs, n) | ||
_nth_inf_cycle(::SizeUnknown, itr, n) = _fallback_nth(itr.xs, n) | ||
_nth_inf_cycle(::Union{HasShape,HasLength}, itr, n) = _repeating_cycle_nth(itr.xs, n, length(itr.xs)) | ||
|
||
# finite cycle | ||
# a finite cycle iterator is in reality a Flatten{Take{Repeated{O}}} iterator | ||
nth(itr::Flatten{Take{Repeated{O}}}, n::Integer) where {O} = _nth_finite_cycle(IteratorSize(O), itr, n) | ||
_nth_finite_cycle(::IsInfinite, itr, n) = _inbounds_nth(itr, n) | ||
_nth_finite_cycle(::SizeUnknown, itr, n) = _fallback_nth(itr, n) | ||
_nth_finite_cycle(::Union{HasShape,HasLength}, itr, n) = begin | ||
N = itr.it.n # `Take` iterator n | ||
torepeat = itr.it.xs.x # repeated object | ||
K = length(torepeat) | ||
n > K * N && return nothing | ||
_repeating_cycle_nth(torepeat, n, K) | ||
end | ||
|
||
|
||
_repeating_cycle_nth(inner_itr, n, inner_N) = _inbounds_nth(inner_itr, 1 + ((n - 1) % inner_N)) | ||
|
||
|
||
|
||
end |
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.
Returning
nothing
makes it impossible to distinguish between "the nth element wasnothing
", and "there was no nth element". Perhaps returnUnion{Nothing, Some}
?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.
Fair point.
Should it be
Union{nothing, Some}
even in those cases where we know there can't be anothing
value in the iterator (for sake of uniform api)? I.e.Count
Iterator orRepeated
(with its element different than nothing) orAbstractRanges
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 think it should, otherwise it would be too confusing.
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 would just throw an error if there is no
n
th element. There could also be adefault
argument as inget
, where a user can pass a value that should be returned if non
th element exists.I don't really follow the logic that the spirit of iterators is to return
nothing
in such cases?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.
Agree
nothing
is weird, your iterator can produce that.Some
seems a bit technical & unfriendly? An error seems fine. Matches whatfirst([])
does.I suppose it can't literally be a method of
get
since it goes by enumeration not keys: