Skip to content

Commit

Permalink
Update Sort module documentation (#25956)
Browse files Browse the repository at this point in the history
Update the sort module documentation based on the work done in the sort
module stabilization subteam.

On a high level
- Added a short intro with a few examples of using `proc sort`, before
this change it was a bit wierd to just see Comparators as the first
thing in the sort module docs.
- Edited the comparators section to mention the new prescribed way to
create and use custom comparators using the newly created interfaces.
- Went over the rest of the doc to update other things that have changed,
like argument names and casing 

[Reviewed by @jabraham17 , thanks!]
  • Loading branch information
ShreyasKhandekar committed Sep 18, 2024
2 parents 24a68ca + 28641b9 commit 84af6b7
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 49 deletions.
6 changes: 6 additions & 0 deletions modules/standard/List.chpl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
- clear
- sort
.. warning::
:proc:`list.sort<List.list.sort>` is deprecated - please use the
:proc:`sort(x: list)<Sort.sort>` procedure from the
:mod:`Sort` module instead
Additionally, all references to list elements are invalidated when the list
is deinitialized.
Expand Down
185 changes: 136 additions & 49 deletions modules/standard/Sort.chpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,104 @@
// TODO -- performance test sort routines and optimize (see other TODO's)
/*
Supports standard algorithms for sorting data.
This module supports standard algorithms for sorting data.
It is designed to
be flexible and efficient, allowing the user to define custom comparators to
sort any data type, as long as the comparator implements the appropriate
sorting interface.
The simplest way to sort an array is to call the :proc:`sort` function on the
array. The sort function will use the default comparator to sort the array in
ascending order.
.. code-block:: chapel
var Array = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
sort(Array);
// This will output: 1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9
writeln(Array);
The sort function can also accept a region argument to sort a subset of an
array. This is offered as an optimization over using an array slice which may
have performance overhead.
.. code-block:: chapel
var Array = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
// Sort only the elemets in the range 1..5
// Same as sort(Array[1..5]);
sort(Array, region=1..5);
// This will output: 3, 1, 1, 4, 5, 9, 2, 6, 5, 3, 5
writeln(Array);
The sort function can also be called on a list, be stable or unstable, and
accept a custom comparator.
See the :proc:`sort(x: list)<Sort.sort>` function for details.
.. _comparators:
Comparators
-----------
Comparators allow sorting data by a mechanism other than the
default comparison operations between array elements. To use a comparator,
define a record or a class with an appropriate method and then pass
an instance of it to the sort function. Examples are shown below.
default comparison operations between array elements.
Comparators need to include at least one of the following methods:
The :proc:`sort` function can accept a comparator argument, which defines how
the data is sorted. If no comparator is passed, the default comparator is
used.
* ``key(a)`` -- see `The .key method`_
* ``compare(a, b)`` -- see `The .compare method`_
* ``keyPart(a, i)`` -- see `The .keyPart method`_
Reverse sorting is handled by the :record:`ReverseComparator`.
See :ref:`Reverse Comparator<reverse-comparator>` for details.
See the section below for discussion of each of these methods.
A comparator can contain both ``compare`` and ``keyPart`` methods. In that
event, the sort algorithm will use whichever is appropriate for the algorithm
and expect that they have consistent results.
To use a custom comparator, define a record or a class which implements the
appropriate sorting interface.
It is an error for a comparator to contain a ``key`` method as well as one of
the other methods.
Comparators need to implement one, and only one, of the following interfaces
as well as at least one of their associated methods:
* :interface:`keyComparator` -- see `The keyComparator interface`_
* :interface:`relativeComparator` -- see `The relativeComparator interface`_
* :interface:`keyPartComparator` -- see `The keyPartComparator interface`_
See the section below for discussion of each of these interfaces and methods.
*Future:*
Provide a unified ``sortComparator`` interface, which can represent an
exclusive or (XOR) of the three interfaces above.
The keyComparator interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``keyComparator`` interface is used to sort data by a key value. Records
implementing this interface must define a ``key`` method.
Today, it is an error for a comparator implementing the ``keyComparator``
interface to contain a ``key`` method as well as one of the other methods
that are part of the ``relativeComparator`` or ``keyPartComparator``
interfaces. This restriction might be lifted in future releases.
The .key method
~~~~~~~~~~~~~~~
***************
The ``key(a)`` method accepts 1 argument, which will be an element from the
The ``key(elt)`` method accepts 1 argument, which will be an element from the
array being sorted.
The default key method would look like this:
.. code-block:: chapel
proc DefaultComparator.key(a) {
return a;
proc DefaultComparator.key(elt) {
return elt;
}
Expand All @@ -73,80 +132,99 @@ elements, the user can define a comparator with a key method as follows:
var Array = [-1, -4, 2, 3];
// Empty record serves as comparator
record Comparator { }
// Empty record serves as comparator, implements the keyComparator interface
record absComparator : keyComparator { }
// key method maps an element to the value to be used for comparison
proc Comparator.key(a) { return abs(a); }
proc absComparator.key(elt) { return abs(elt); }
var absComparator: Comparator;
var absoluteComparator: absComparator;
sort(Array, comparator=absComparator);
sort(Array, comparator=absoluteComparator);
// This will output: -1, 2, 3, -4
writeln(Array);
The return type of ``key(a)`` must support the ``<``
The return type of ``key(elt)`` must support the ``<``
operator, which is used by the base compare method of all sort routines. If the
``<`` operator is not defined for the return type, the user may define it
themselves like so:
.. code-block:: chapel
operator <(a: returnType, b: returnType): bool {
operator <(x: returnType, y: returnType): bool {
...
}
The relativeComparator interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``relativeComparator`` interface is used to sort data by comparing two
elements directly. Records implementing this interface must define a
``compare`` method.
The .compare method
~~~~~~~~~~~~~~~~~~~
*******************
The ``compare(a, b)`` method accepts 2 arguments, which will be 2 elements from
The ``compare(x, y)`` method accepts 2 arguments, which will be 2 elements from
the array being sorted. The return value should be a numeric signed type
indicating how a and b compare to each other. The conditions between ``a`` and
``b`` should result in the following return values for ``compare(a, b)``:
indicating how x and y compare to each other. The conditions between ``x`` and
``y`` should result in the following return values for ``compare(x, y)``:
============ ==========
Return Value Condition
============ ==========
``> 0`` ``a > b``
``0`` ``a == b``
``< 0`` ``a < b``
``> 0`` ``x > y``
``0`` ``x == y``
``< 0`` ``x < y``
============ ==========
The default compare method for a signed integral type can look like this:
.. code-block:: chapel
proc DefaultComparator.compare(a, b) {
return a - b;
proc DefaultComparator.compare(x, y) {
return x - y;
}
The absolute value comparison example from above can alternatively be
implemented with a compare method:
implemented with a ``relativeComparator`` as follows:
.. code-block:: chapel
var Array = [-1, -4, 2, 3];
// Empty record serves as comparator
record Comparator { }
record absComparator : relativeComparator { }
// compare method defines how 2 elements are compared
proc Comparator.compare(a, b) {
return abs(a) - abs(b);
proc absComparator.compare(x, y) {
return abs(x) - abs(y);
}
var absComparator: Comparator;
var absoluteComparator: absComparator;
sort(Array, comparator=absComparator);
sort(Array, comparator=absoluteComparator);
// This will output: -1, 2, 3, -4
writeln(Array);
The keyPartComparator interface
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The keyPartComparator interface defines how a comparator should sort parts of
a key using the ``keyPart`` method. This is used for certain sort algorithms.
Records implementing this interface must define a ``keyPart`` method.
A comparator implementing this interface can optionally also provide a
`compare` method. In that event, the sort algorithm will use whichever is
appropriate for the algorithm and expect that they have consistent results.
The .keyPart method
~~~~~~~~~~~~~~~~~~~
*******************
A ``keyPart(elt, i)`` method returns *parts* of key value at a time. This
interface supports radix sorting for variable length data types, such as
Expand Down Expand Up @@ -176,7 +254,7 @@ This ``keyPart`` method supports sorting tuples of 2 integers:
proc keyPart(elt: 2*int, i: int) {
if i > 1 then
return (-1, 0);
return (keyPartStatus.pre, 0); // second value is not used
return (keyPartStatus.returned, elt(i));
}
Expand Down Expand Up @@ -220,26 +298,31 @@ To reverse the sort order of a user-defined comparator, pass the user-defined
comparator to the initializer of the module-defined
:record:`ReverseComparator` record, which can be passed to the sort function.
For this example, we will reverse the absolute value comparison from above
using the ``relativeComparator`` interface, although the same can be done with
the ``keyComparator`` interface.
.. code-block:: chapel
var Array = [-1, -4, 2, 3];
// Empty record serves as comparator
record Comparator { }
record absComparator : relativeComparator{ }
// compare method defines how 2 elements are compared
proc Comparator.compare(a, b) {
return abs(a) - abs(b);
proc absComparator.compare(x, y) {
return abs(x) - abs(y); // ascending order
}
var absReverseComparator: ReverseComparator(Comparator);
var absReverseComparator: ReverseComparator(absComparator); // reverse order
sort(Array, comparator=absReverseComparator);
// This will output: -4, 3, 2, -1
writeln(Array);
*/

module Sort {

private use List;
Expand Down Expand Up @@ -637,7 +720,8 @@ The choice of sorting algorithm used is made by the implementation.
.. note::
This function currently either uses a parallel radix sort or a parallel
improved quick sort. The algorithms used will change over time.
improved quick sort. For stable sort, it uses Timsort.
The algorithms used will change over time.
It currently uses parallel radix sort if the following conditions are met:
Expand Down Expand Up @@ -683,7 +767,7 @@ proc sort(ref x: [], comparator:? = new DefaultComparator(),
Sort the elements in the list ``x``. After the call, ``x`` will store elements
in sorted order.
See the :proc:`sort` declared just above for details.
See :proc:`sort` declared above for details.
.. warning::
Expand Down Expand Up @@ -765,7 +849,8 @@ The choice of sorting algorithm used is made by the implementation.
.. note::
This function currently either uses a parallel radix sort or a parallel
improved quick sort. The algorithms used will change over time.
improved quick sort. For stable sort, use :proc:`sort` with ``stable=true``.
The algorithms used will change over time.
It currently uses parallel radix sort if the following conditions are met:
Expand Down Expand Up @@ -806,6 +891,8 @@ proc sort(ref Data: [?Dom] ?eltType, comparator:?rec=defaultComparator,
chpl_check_comparator(comparator, eltType);

if stable {
// TODO: we already have a stable sort, but it is not called here
// maybe we should call it here, even though this one is deprecated
// TODO: implement a stable merge sort with parallel merge
// TODO: create an in-place merge sort for the stable+minimizeMemory case
// TODO: create a stable variant of the radix sort
Expand Down

0 comments on commit 84af6b7

Please sign in to comment.