-
-
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
Fix findmax and findmin with iterables #14086
Conversation
dd8a737
to
dc55d04
Compare
iold = i | ||
ai, i = next(a, i) | ||
ai, s = next(a, s) | ||
if ai > m || m!=m | ||
m = ai | ||
mi = iold |
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 could just be i
and you could get rid of iold
.
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.
Of course. I fixed the code a bit too directly.
Yes, since you're now using an integer counter, it's fine to get rid of |
dc55d04
to
2b7e75c
Compare
The state is not guaranteed to be equivalent to the index of the element.
2b7e75c
to
bbaaf54
Compare
@timholy I wonder why you didn't write the code that way in the first place? Did you have a more complex case in mind? Or for performance? My version sounds so terribly simple. :-) I've updated the PR. |
Actually, this PR loses the support for array indexing, it will only return the linear index. It looks like a simple solution would be to have the current method as a fallback for generic iterables, and add a specialized method for We also need to make a decision as regards backporting, as this is technically an API break -- even if leaving it broken for ranges isn't acceptable either. |
Nah, I think your version is just plain better 😄. I suspect I was wrestling with the idea of whether we should be returning the state (e.g., a Thanks for doing this! |
I think But you're right that a better solution would be nice. |
@timholy OK, cool. This commit is good for backporting. But I wonder whether we shouldn't improve the behavior in master and return a cartesian index for arrays. That sounds more natural than a linear index (and indeed if isa(a, AbstractArray)
return (m, ind2sub(size(a), mi))
else
return (m, mi)
end |
That wouldn't be type-stable, but it wouldn't be hard to make it so. In general I like the idea of returning a native iterator. One issue, though, is that this simple solution will break if/when we switch to supporting arrays with arbitrary indexes (i.e., not just |
Why wouldn't it be type-stable? The behavior would depend only on the type of the array. Regarding arrays with arbitrary indexes, we would have to take into account not only the size of the array, but the array itself (or its type) so that a specialized method could translate the index correctly. I can add a, say, I've also discovered one possible failure case: the one in which the iterable is also indexable, but the indexes differ from the rank of the elements. The only case I know of at the moment is strings, for which |
While this may change someday, julia> function foo(x)
if isa(x, AbstractArray)
return 1
else
return 1.0
end
end
foo (generic function with 1 method)
julia> @code_warntype foo(rand(2,2))
Variables:
x::Array{Float64,2}
Body:
begin # none, line 2:
unless (Main.isa)(x::Array{Float64,2},Main.AbstractArray)::Bool goto 0 # none, line 3:
return 1
goto 1
0: # none, line 5:
return 1.0
1:
end::Union{Float64,Int64} But if you define it as
then it is type-stable. |
This is getting pretty close to the implementation we have now---the only difference is that I held onto the state and called Maybe a better fix would be to leave the current one in place and write a special exception for Ranges? Really, for |
I don't think we can get away with this by making an exception for ranges: the fallback implementation should work in all cases and not rely on assumptions that are not in the API contract of iterables to support all possible custom types. (Of course, a more efficient implementation for ranges would be useful for other purposes.) My proposal differs from the current implementation in two ways:
|
I guess there's still a part of me that wonders whether the |
But can I always call (As regards the iteration state, we might consider requiring that it is equal to the index for indexable types.) |
It would be a no-op for
I wonder about that too. Anyone else want to chime in about whether this would be overly restrictive? Obviously Ranges would need some restructuring. |
I'm not sure how I don't think we should be in the business of prescribing the format of iteration state. Better in that case to introduce a function that will translate it into an index or similar, based on the type that is iterated over. |
+1 to what @toivoh said. |
Sounds like we shouldn't assume that the state can be used as an index. So I'd say go ahead with your plan, @nalimilan, and we'll deal with difficulties from generalization as (or if) they arise. |
OK. Could you tell me how I can extract the index tuple from the |
julia/base/multidimensional.jl Line 18 in 9c90d2c
|
Thanks, I confused this simple structure with the more complex iterator state. Though there are more issues to tackle:
At least always returning a linear index was simple. :-) |
I don't know whether this makes it more or less complicated, but there's |
Maybe one could specify the desired output type, e.g., findmax(Int, a)
findmax(CartesianIndex, a) |
That would be a possibility (and could also lead to replace So I can see two solutions, to apply identically to
In both cases, we can optionally support passing We can also consider in this discussion |
Yes, I also favor returning a linear index by default. |
So, let's say we go with (and backport) this PR, and will tackle the question of |
👍 |
Fix findmax and findmin with iterables
The state is not guaranteed to be equivalent to the index of the element.
Fixes #14085
I wonder whether
iterstate
could be removed, as it doesn't appear to be used anywhere after this change, and it's potentially misleading.