From be54a6c4a3a90dcf4007df3976f37465eb817d87 Mon Sep 17 00:00:00 2001 From: Michael Jared Lumpe Date: Tue, 29 Sep 2020 12:36:22 -0600 Subject: [PATCH] Add documentation for Base.Order module (#36886) * Add docstrings for Base.Order module * Add documentation section for Base.Order * Add reference to documentation on Base.Order in isless() docstring --- base/operators.jl | 2 ++ base/ordering.jl | 68 ++++++++++++++++++++++++++++++++++++++++++++ doc/src/base/sort.md | 29 +++++++++++++++++++ 3 files changed, 99 insertions(+) diff --git a/base/operators.jl b/base/operators.jl index 060cb59c926d0..bdf1735bad884 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -150,6 +150,8 @@ This is the default comparison used by [`sort`](@ref). Non-numeric types with a total order should implement this function. Numeric types only need to implement it if they have special values such as `NaN`. Types with a partial order should implement [`<`](@ref). +See the documentation on [Alternate orderings](@ref) for how to define alternate +ordering methods that can be used in sorting and related functions. # Examples ```jldoctest diff --git a/base/ordering.jl b/base/ordering.jl index dc2f2c8595be2..6dad6925f51d2 100644 --- a/base/ordering.jl +++ b/base/ordering.jl @@ -18,9 +18,26 @@ export # not exported by Base DirectOrdering, lt, ord, ordtype +""" + Base.Order.Ordering + +Abstract type which represents a total order on some set of elements. + +Use [`Base.Order.lt`](@ref) to compare two elements according to the ordering. +""" abstract type Ordering end struct ForwardOrdering <: Ordering end + +""" + ReverseOrdering(fwd::Ordering=Forward) + +A wrapper which reverses an ordering. + +For a given `Ordering` `o`, the following holds for all `a`, `b`: + + lt(ReverseOrdering(o), a, b) == lt(o, b, a) +""" struct ReverseOrdering{Fwd<:Ordering} <: Ordering fwd::Fwd end @@ -31,9 +48,26 @@ ReverseOrdering() = ReverseOrdering(ForwardOrdering()) const DirectOrdering = Union{ForwardOrdering,ReverseOrdering{ForwardOrdering}} +""" + Base.Order.Forward + +Default ordering according to [`isless`](@ref). +""" const Forward = ForwardOrdering() + +""" + Base.Order.Reverse + +Reverse ordering according to [`isless`](@ref). +""" const Reverse = ReverseOrdering() +""" + By(by, order::Ordering=Forward) + +`Ordering` which applies `order` to elements after they have been transformed +by the function `by`. +""" struct By{T, O} <: Ordering by::T order::O @@ -42,10 +76,23 @@ end # backwards compatibility with VERSION < v"1.5-" By(by) = By(by, Forward) +""" + Lt(lt) + +`Ordering` which calls `lt(a, b)` to compare elements. `lt` should +obey the same rules as implementations of [`isless`](@ref). +""" struct Lt{T} <: Ordering lt::T end +""" + Perm(order::Ordering, data::AbstractVector) + +`Ordering` on the indices of `data` where `i` is less than `j` if `data[i]` is +less than `data[j]` according to `order`. In the case that `data[i]` and +`data[j]` are equal, `i` and `j` are compared by numeric value. +""" struct Perm{O<:Ordering,V<:AbstractVector} <: Ordering order::O data::V @@ -54,6 +101,11 @@ end ReverseOrdering(by::By) = By(by.by, ReverseOrdering(by.order)) ReverseOrdering(perm::Perm) = Perm(ReverseOrdering(perm.order), perm.data) +""" + lt(o::Ordering, a, b) + +Test whether `a` is less than `b` according to the ordering `o`. +""" lt(o::ForwardOrdering, a, b) = isless(a,b) lt(o::ReverseOrdering, a, b) = lt(o.fwd,b,a) lt(o::By, a, b) = lt(o.order,o.by(a),o.by(b)) @@ -78,6 +130,22 @@ function _ord(lt, by, order::Ordering) end end +""" + ord(lt, by, rev::Union{Bool, Nothing}, order::Ordering=Forward) + +Construct an [`Ordering`](@ref) object from the same arguments used by +[`sort!`](@ref). +Elements are first transformed by the function `by` (which may be +[`identity`](@ref)) and are then compared according to either the function `lt` +or an existing ordering `order`. `lt` should be [`isless`](@ref) or a function +which obeys similar rules. Finally, the resulting order is reversed if +`rev=true`. + +Passing an `lt` other than `isless` along with an `order` other than +[`Base.Order.Forward`](@ref) or [`Base.Order.Reverse`](@ref) is not permitted, +otherwise all options are independent and can be used together in all possible +combinations. +""" ord(lt, by, rev::Nothing, order::Ordering=Forward) = _ord(lt, by, order) function ord(lt, by, rev::Bool, order::Ordering=Forward) diff --git a/doc/src/base/sort.md b/doc/src/base/sort.md index ed6b5afab3e12..9f00381ab892c 100644 --- a/doc/src/base/sort.md +++ b/doc/src/base/sort.md @@ -188,3 +188,32 @@ defalg(v::AbstractArray{<:Number}) = QuickSort As for numeric arrays, choosing a non-stable default algorithm for array types for which the notion of a stable sort is meaningless (i.e. when two values comparing equal can not be distinguished) may make sense. + +## Alternate orderings + +By default, `sort` and related functions use [`isless`](@ref) to compare two +elements in order to determine which should come first. The +[`Base.Order.Ordering`](@ref) abstract type provides a mechanism for defining +alternate orderings on the same set of elements. Instances of `Ordering` define +a [total order](https://en.wikipedia.org/wiki/Total_order) on a set of elements, +so that for any elements `a`, `b`, `c` the following hold: + +* Exactly one of the following is true: `a` is less than `b`, `b` is less than + `a`, or `a` and `b` are equal (according to [`isequal`](@ref)). +* The relation is transitive - if `a` is less than `b` and `b` is less than `c` + then `a` is less than `c`. + +The [`Base.Order.lt`](@ref) function works as a generalization of `isless` to +test whether `a` is less than `b` according to a given order. + +```@docs +Base.Order.Ordering +Base.Order.lt +Base.Order.ord +Base.Order.Forward +Base.Order.ReverseOrdering +Base.Order.Reverse +Base.Order.By +Base.Order.Lt +Base.Order.Perm +```