-
-
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
require explicit predicates in find
functions
#23812
Conversation
Also, I didn't touch |
base/deprecated.jl
Outdated
@@ -1855,6 +1855,12 @@ end | |||
nothing | |||
end | |||
|
|||
@deprecate find(x::Number) find(x->x!=0, x) |
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.
Should we encourage find(!iszero, x)
for this?
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.
Ah yes, for Number
I guess we should.
test/arrayops.jl
Outdated
@@ -427,44 +427,43 @@ end | |||
@test X[Y[end],1] == 5 | |||
@test X[end,Y[end]] == 11 | |||
end | |||
equalto(y) = x->x==y |
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.
Would it make sense to use Base.equalto
for these, or do you specifically need ==
rather than isequal
for these tests?
I really like |
While it shouldn't block anything, I think it's a little strange that |
That's actually only the version in the tests --- I'll replace that. The one I put in Base uses |
060d3c4
to
ebb3e05
Compare
base/deprecated.jl
Outdated
@@ -1855,6 +1855,12 @@ end | |||
nothing | |||
end | |||
|
|||
@deprecate find(x::Number) find(!iszero, x) | |||
@deprecate findnext(A, v, i::Integer) findnext(x->x==v, A, i) |
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.
Perhaps we should export equalto
and use it in the deprecation targets?
f151e89
to
6253be9
Compare
Ok, I went ahead and exported |
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.
Jeff does it again! 💯
Ah, this needs a NEWS item for the addition of |
@@ -1615,8 +1615,14 @@ julia> findnext(A,3) | |||
function findnext(A, start::Integer) | |||
l = endof(A) | |||
i = start | |||
warned = false |
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.
Won't this warn everytime the function is called?
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 was counting on depwarn
for that.
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.
Yeah, this looks good to me — the warned
makes sure the slower depwarn
is only called once for each invocation.
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.
Oh, sorry for the noise.
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.
That's really a nice simplification of the API.
Also, I didn't touch findn, findnz, or find for sparse matrices. I suppose for find, we could require that predicates be false for 0 (giving an error otherwise).
findn
and findnz
are special, so it's fine to keep them (at least for now), but having find
behave differently for sparse matrices doesn't sound like a good idea to me. We could at least change the signature of the current find(::SparseMatrixCSC)
method to find(::Union{typeof(!iszero), typeof(!equalto(0))}, ::SparseMatrixCSC)
to ensure consistency and still benefit from an optimized implementation for the common case.
That approach can be generalized this by checking whether the predicate is true
or false
on zero elements and assuming it's pure. I don't think throwing an error is a good idea: as discussed elsewhere, sparse matrices should behave the same as dense ones everywhere it's possible.
base/array.jl
Outdated
@@ -1596,14 +1596,14 @@ cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x | |||
""" | |||
findnext(A, i::Integer) | |||
|
|||
Find the next linear index >= `i` of a non-zero element of `A`, or `0` if not found. | |||
Find the next linear index >= `i` of a true element of `A`, or `0` if not found. |
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.
Maybe add backticks around true
(here and in other docstring) since "a true element" may not be completely obvious when you have no idea what the function does.
base/array.jl
Outdated
@@ -1596,14 +1596,14 @@ cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x | |||
""" | |||
findnext(A, i::Integer) | |||
|
|||
Find the next linear index >= `i` of a non-zero element of `A`, or `0` if not found. | |||
Find the next linear index >= `i` of a true element of `A`, or `0` if not found. | |||
|
|||
# Examples |
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.
It would make sense to add an example using equalto
, since people will likely look at this docstring when they need this feature (same for other docstrings). Or just mention it in the description.
base/file.jl
Outdated
@@ -271,7 +271,7 @@ function tempname(temppath::AbstractString,uunique::UInt32) | |||
tempp = cwstring(temppath) | |||
tname = Vector{UInt16}(32767) | |||
uunique = ccall(:GetTempFileNameW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32,Ptr{UInt16}), tempp,temp_prefix,uunique,tname) | |||
lentname = findfirst(tname,0)-1 | |||
lentname = findfirst(equalto(0),tname)-1 |
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.
Wouldn't iszero
work here?
base/inference.jl
Outdated
@@ -5604,7 +5604,7 @@ function _getfield_elim_pass!(e::Expr, sv::InferenceState) | |||
if alloc !== false | |||
flen, fnames = alloc | |||
if isa(j, QuoteNode) | |||
j = findfirst(fnames, j.value) | |||
j = findfirst(x->x == j.value, fnames) |
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.
Wouldn't equalto
be enough? Same below.
base/operators.jl
Outdated
""" | ||
equalto(x) | ||
|
||
Create a function that compares its argument to `x` using `isequal`; i.e. returns |
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.
[`isequal`](@ref)
base/pkg/query.jl
Outdated
@@ -359,7 +359,7 @@ function prune_versions(reqs::Requires, deps::Dict{String,Dict{VersionNumber,Ava | |||
vmaskp[vn] = falses(luds) | |||
end | |||
for (vn,a) in fdepsp | |||
vmind = findfirst(uniqdepssets, a.requires) | |||
vmind = findfirst(Base.equalto(a.requires), uniqdepssets) |
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.
Why Base.
?
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.
Shouldn't be necessary since it's exported?
Thanks for the good review @nalimilan .
The only reason I want to throw an error is so I don't have to implement a new algorithm for finding zeros. That case can be added later. |
if A[i] != 0 | ||
a = A[i] | ||
if !warned && !(a isa Bool) | ||
depwarn("In the future `findnext` will only work on boolean collections. Use `findnext(x->x!=0, A)` instead.", :findnext) |
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.
When I've done things like this I've often added breadcrumbs that point to it from deprecated.jl that help make sure they get removed at the appropriate timeframe.
Ah --- previously |
6253be9
to
1eb0827
Compare
@nanosoldier |
Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan |
In UniqueVectors.jl I override Overall I am on the fence, leaning toward being -1 about this change. As for #19186, I've never found the argument order confusing, as long as I keep in mind that predicates go first so that the do-block syntax can be supported. |
Or perhaps this would solve my issue: struct EqualTo{T}
val::T
end
(x::EqualTo)(y) = isequal(x.val,y)
equalto(x) = EqualTo(x) Then I could override |
If this PR lands, it would also be good to see a test where |
To make What I find confusing about the current methods is that in |
d67a0b1
to
25ce69f
Compare
Done --- I just made |
base/operators.jl
Outdated
@@ -972,3 +972,22 @@ julia> filter(!isalpha, str) | |||
``` | |||
""" | |||
!(f::Function) = (x...)->!f(x...) | |||
|
|||
struct equalto{T} <: Function |
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 should probably use the typical upper camel case style for type names as recommended in the style guide and used in Base, especially since this is user-facing.
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.
Disagree, since this is meant to be used like a function, equalto(x)
.
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.
You can always define equalto(x) = EqualTo(x)
, as is done with Iterators.filter
returning a Filter
object.
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.
A problem with conflating equalto
with "regular" functions is that it doesn't have its own type in the same way that other functions do. In particular, to specialize on a particular function, you use ::typeof(f)
, but in this case typeof(equalto)
will be DataType
. So users will have to remember that to specialize on this function they need ::equalto
instead.
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 agree with @ararslan, that pattern is quite common, we also use it with zip
vs. Zip1
/Zip2
.
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.
True, but I'm not sure that's such a great pattern. It's confusing to have both filter
and Filter
. That came from a situation where filter
had several methods, only one of which returned a Filter
, but then the functions were separated (into Base.filter
and Base.Iterators.filter
). zip
is also different in that it might return either a Zip1
or a Zip2
, so it can't be directly identified with either type.
That's right --- @garrison 's use case would dispatch as |
Still seems simpler and more consistent to me to do what we do in |
But then why don't we have |
Okay, then if this is supposed to be used as a straight type constructor rather than an accessor function, I think we should go with the stylistic conventions and call it |
How about |
|
Actually I got my last comment backwards. We could call the type |
25ce69f
to
9bdf07f
Compare
This fixes the deprecations due to JuliaLang/julia#23812 and makes for cleaner code anyway. (Truncation of trailing zeros is done in the `Poly` constructor so there is no need to do it here.)
This fixes the deprecations due to JuliaLang/julia#23812 and makes for cleaner code anyway. (Truncation of trailing zeros is done in the `Poly` constructor so there is no need to do it here.)
if A[i] != 0 | ||
a = A[i] | ||
if !warned && !(a isa Bool) | ||
depwarn("In the future `findnext` will only work on boolean collections. Use `findnext(x->x!=0, A)` instead.", :findnext) |
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.
Should start
be an argument to findnext
in the depwarn message?
depwarn(" ... Use `findnext(x->x!=0, A, start)` instead.", :findnext)
Similarly for findprev
?
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.
Indeed, it should.
…e explicit Since we now need explicit predicates [1], this optimization only works if we know that the predicate is a function that is false for zero values. As suggested in that pull request, we could find out by calling `f(zero(eltype(array)))` and hoping that `f` is pure, but I like being a bit more conservative and only applying this optimization only to the case where we *know* `f` is equal to `!iszero`. For clarity, this commit also renames the helper method _sparse_findnext() to _sparse_findnextnz(), because now that the predicate-less version doesn't exist anymore, the `nz` part isn't implicit anymore either. [1]: JuliaLang#23812
Were these methods deliberately omitted, or was that an oversight? If the latter, I will likely get them as part of moving towards using I'm contemplating adding a If people like that, I'd be tempted to include that change among the deprecations too, because otherwise I may have to add a bunch of specializations just to resolve ambiguities. There's some risk that this would change method sorting in a breaking way for packages |
Good catch! These should definitely be deprecated, I must have missed them. I'm not sure we need to specify start Feel free to put everything in a PR it that makes things simpler for you. |
I also tried out using
equalto(x)
as a predicate, and I really think we should adopt it. It reads nicely (!equalto(x)
also works) and will cut down the number of function types.fix #23120, fix #19186
Part of #10593.