Skip to content

Commit 96433cf

Browse files
committed
define isgreater to be compatible with min
1 parent 8061ce8 commit 96433cf

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

base/operators.jl

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -171,20 +171,51 @@ isless(x::AbstractFloat, y::Real ) = (!isnan(x) & (isnan(y) | signless(x
171171
"""
172172
isgreater(x, y)
173173
174-
Test whether `x` is greater than `y`, according to a fixed total order.
175-
`isgreater` is defined in terms of `isless`, but is not the opposite of that function.
174+
Not the inverse of `isless`! Test whether `x` is greater than `y`, according to
175+
a fixed total order compatible with `min`.
176176
177-
`isless` defines a fixed total order that ascends, while `isgreater` defines a
178-
fixed total order that descends. Both order values that are normally unordered,
179-
such as `NaN`, after all regular values and [`missing`](@ref) last. So for
180-
`isless` these values are biggest and for `isgreater` these values are
181-
smallest.
177+
Defined with `isless`, this function is usually `isless(y, x)`, but `NaN` and
178+
[`missing`](@ref) are ordered as smaller than any ordinary value with `missing`
179+
smaller than `NaN`.
180+
181+
So `isless` defines an ascending total order with `NaN` and `missing` as the
182+
largest values and `isgreater` defines a descending total order with `NaN` and
183+
`missing` as the smallest values.
184+
185+
!!! note
186+
187+
Like `min`, `isgreater` orders containers (tuples, vectors, etc)
188+
lexigraphically with `isless(y, x)` rather than recursively with itself:
189+
190+
```jldoctest
191+
julia> isgreater(1, NaN) # 1 is greater than NaN
192+
true
193+
194+
julia> isgreater((1,), (NaN,)) # But (1,) is not greater than (NaN,)
195+
false
196+
197+
julia> sort([1, 2, 3, NaN]; lt=isgreater)
198+
4-element Vector{Float64}:
199+
3.0
200+
2.0
201+
1.0
202+
NaN
203+
204+
julia> sort(tuple.([1, 2, 3, NaN]); lt=isgreater)
205+
4-element Vector{Tuple{Float64}}:
206+
(NaN,)
207+
(3.0,)
208+
(2.0,)
209+
(1.0,)
210+
```
182211
183212
# Implementation
184-
Types should not usually implement this function. Instead, implement `isless`.
213+
This is unexported. Types should not usually implement this function. Instead, implement `isless`.
185214
"""
186-
isgreater(x, y) = _is_reflexive_eq(x) && _is_reflexive_eq(y) ? isless(y, x) : isless(x, y)
187-
_is_reflexive_eq(x) = (x == x) === true
215+
isgreater(x, y) = is_poisoning(x) || is_poisoning(y) ? isless(x, y) : isless(y, x)
216+
is_poisoning(x) = false
217+
is_poisoning(x::AbstractFloat) = isnan(x)
218+
is_poisoning(x::Missing) = true
188219

189220
function ==(T::Type, S::Type)
190221
@_pure_meta

test/operators.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,16 @@ import Base.<
8484
@test isless('a','b')
8585

8686
@testset "isgreater" begin
87-
isgreater = Base.isgreater
88-
@test !isgreater(missing, 1)
89-
@test isgreater(5, 1)
90-
@test !isgreater(1, 5)
91-
@test isgreater(1, missing)
92-
@test isgreater(1, NaN)
87+
# isgreater should be compatible with min.
88+
min1(a, b) = isgreater(a, b) ? b : a
89+
# min promotes numerical arguments to the same type, but our quick min1
90+
# doesn't, so use float test values instead of ints.
91+
values = (1.0, 5.0, NaN, missing, Inf)
92+
for a in values, b in values
93+
@test min(a, b) === min1(a, b)
94+
@test min((a,), (b,)) === min1((a,), (b,))
95+
@test all(min([a], [b]) .=== min1([a], [b]))
96+
end
9397
end
9498

9599
@testset "vectorized comparisons between numbers" begin

0 commit comments

Comments
 (0)