-
-
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
Define last
for non-O(1) types?
#42943
Comments
Copying my original question on Zulip here: Is this a case of "we don't know in advance what the latst element would be in the general case?" and that's why it errors? or should this be defined? julia> Iterators.map(x -> x^2, 1:3)
Base.Generator{UnitRange{Int64}, var"#2#3"}(var"#2#3"(), 1:3)
julia> m = Iterators.map(x -> x^2, 1:3)
Base.Generator{UnitRange{Int64}, var"#4#5"}(var"#4#5"(), 1:3)
julia> first(m)
1
julia> last(m)
ERROR: MethodError: no method matching lastindex(::Base.Generator{UnitRange{Int64}, var"#4#5"}) Seems like it could be known without collecting all the values? julia> dump(m)
Base.Generator{UnitRange{Int64}, var"#4#5"}
f: #4 (function of type var"#4#5")
iter: UnitRange{Int64}
start: Int64 1
stop: Int64 3
julia> m.f(m.iter.stop)
9 |
Do you want julia> foldlast(x) = foldl((_, r) -> r, x);
julia> itr = Iterators.Stateful("abc");
julia> foldlast(itr)
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
julia> foldlast(itr)
ERROR: ArgumentError: reducing over an empty collection is not allowed Though I'm not sure if that is a sufficient argument against this, because stateful iterators can often be counter-intuitive if you're not careful about the statefulness. |
Yeah, stateful iterators definitely make this dangerous, certainty it’d make it so that @alecloudenback ’s suggestion can’t be done in any but the most restrictive of cases, (though if it’s necessary, one can use MappedArrays.jl) I think though that the existence of stateful iterators don’t stop this from being useful so long as it’s carefully documented. For instance, the julia> x = Iterators.Stateful("hello")
Base.Iterators.Stateful{String, Union{Nothing, Tuple{Char, Int64}}}("hello", ('h', 2), 0)
julia> first(x)
'h': ASCII/Unicode U+0068 (category Ll: Letter, lowercase)
julia> first(x)
'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase) |
1.6 brought us Is there a motivating example for this, other than "we could do it for |
Right, and I am suggesting a similar fallback (not a special case) for There are cases with finite iterators (including stateful ones) where one might only be interested in the final element. E.g. one example I saw recently was an iterator that generates the first struct Fibn
n::Int
end
import Base: iterate, length
length(f::Fibn)=f.n
iterate(f::Fibn) = (0,(0, 1, 1))
iterate(f::Fibn, st) = st[3] < f.n ? (st[2], (st[2], st[1]+st[2],st[3]+1)) : nothing This thing can be |
I don't think an O(1) fallback method for We could use |
That sounds reasonable to me. |
I think you meant O(n) here :) |
Currently, our fallback definition of
last
islast(x) = x[end]
and the docstring for it saysIt was brought up on Zulip today (and I've also been personally bitten by this in the past) that this doesn't work for lazy iterators, which can be kinda annoying.
I was playing around and found to my surprise that the compiler is smart enough in every case I could find except two to make
foldl((_, r) -> r, x)
just as fast aslast(x)
for types with O(1) indexing, while also naturally generalizing to non-O(1) collections.Here are the cases I tried where it gets this right:
and here are two cases where it loses:
The
Pair
case is quite bizarre and maybe should be a separate issue(?).Anyways, this suggests to me that maybe the fallback definition of
last
should be to justfoldl
over the collection, allowing it to be used on types which don't support indexing, such as general iterators (and transducers!), and this can be done nearly effortlessly, seemingly just requiring that we define an overload forTuple
andPair
that use indexing.Is this something we'd want to do, or is there a good reason to not let this work on non-indexable types?
The text was updated successfully, but these errors were encountered: