emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 4b79c80c999 1/2: New function 'sort-on'


From: Eli Zaretskii
Subject: master 4b79c80c999 1/2: New function 'sort-on'
Date: Fri, 2 Feb 2024 08:27:56 -0500 (EST)

branch: master
commit 4b79c80c999fe95654b7db196b12e0844473f6da
Author: Eli Zaretskii <eliz@gnu.org>
Commit: Eli Zaretskii <eliz@gnu.org>

    New function 'sort-on'
    
    * lisp/sort.el (sort-on): New function.  Patch by John Wiegley
    <jwiegley@gmail.com>.
    
    * etc/NEWS:
    * doc/lispref/sequences.texi (Sequence Functions): Document
    'sort-on'.
---
 doc/lispref/sequences.texi | 37 +++++++++++++++++++++++++++++++++----
 etc/NEWS                   |  5 +++++
 lisp/sort.el               | 20 ++++++++++++++++++++
 3 files changed, 58 insertions(+), 4 deletions(-)

diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index f1f23f007a4..654019dfc31 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -434,12 +434,41 @@ but their relative order is also preserved:
          (9 . "aaa") (9 . "zzz") (9 . "ppp") (9 . "fff")]
 @end group
 @end example
-
-@xref{Sorting}, for more functions that perform sorting.
-See @code{documentation} in @ref{Accessing Documentation}, for a
-useful example of @code{sort}.
 @end defun
 
+Sometimes, computation of sort keys of list elements is expensive, and
+therefore it is important to perform it the minimum number of times.
+By contrast, computing the sort keys of elements inside the
+@var{predicate} function passed to @code{sort} will generally perform
+this computation each time @var{predicate} is called with some
+element.  If you can separate the computation of the sort key of an
+element into a function of its own, you can use the following sorting
+function, which guarantees that the key will be computed for each list
+element exactly once.
+
+@defun sort-on sequence predicate accessor
+This function stably sorts the list @var{sequence}, comparing the sort
+keys of the elements using @var{predicate}.  The comparison function
+@var{predicate} accepts two arguments, the sort keys to compare, and
+should return non-@code{nil} if the element corresponding to the first
+key should sort before the element corresponding to the second key.
+The function computes a sort key of each element by calling the
+@var{accessor} function on that element; it does so exactly once for
+each element of @var{sequence}.  The @var{accessor} function is called
+with a single argument, an element of @var{sequence}.
+
+This function implements what is known as
+@dfn{decorate-sort-undecorate} paradigm, of the Schwartzian transform.
+It basically trades CPU for memory, creating a temporary list with the
+computed sport keys, then mapping @code{car} over the result of
+sorting that temporary list.  Unlike with @code{sort}, the return list
+is a copy; the original list is left intact.
+@end defun
+
+@xref{Sorting}, for more functions that perform sorting.  See
+@code{documentation} in @ref{Accessing Documentation}, for a useful
+example of @code{sort}.
+
 @cindex sequence functions in seq
 @cindex seq library
 @cindex sequences, generalized
diff --git a/etc/NEWS b/etc/NEWS
index 5b3d7dec8a6..816613de4ec 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1530,6 +1530,11 @@ precedence over the variable when present.
 Mostly used internally to do a kind of topological sort of
 inheritance hierarchies.
 
+** New function 'sort-on'.
+This function implements the Schwartzian transform, and is appropriate
+for sorting lists when the computation of the sort key of a list
+element can be expensive.
+
 ** New API for 'derived-mode-p' and control of the graph of major modes.
 
 *** 'derived-mode-p' now takes the list of modes as a single argument.
diff --git a/lisp/sort.el b/lisp/sort.el
index 2ee76b6e1e3..97b40a2aef4 100644
--- a/lisp/sort.el
+++ b/lisp/sort.el
@@ -478,6 +478,26 @@ sRegexp specifying key within record: \nr")
                          ;; if there was no such register
                          (error (throw 'key nil))))))))))
 
+;;;###autoload
+(defun sort-on (sequence predicate accessor)
+  "Sort SEQUENCE by calling PREDICATE on sort keys produced by ACCESSOR.
+SEQUENCE should be the input list to sort.
+Elements of SEQUENCE are sorted by keys which are obtained by
+calling ACCESSOR on each element.  ACCESSOR should be a function of
+one argument, an element of SEQUENCE, and should return the key
+value to be compared by PREDICATE for sorting the element.
+PREDICATE is the function for comparing keys; it is called with two
+arguments, the keys to compare, and should return non-nil if the
+first key should sort before the second key.
+This function has the performance advantage of evaluating
+ACCESSOR only once for each element in the input SEQUENCE, and is
+therefore appropriate when computing the key by ACCESSOR is an
+expensive operation.  This is known as the \"decorate-sort-undecorate\"
+paradigm, or the Schwartzian transform."
+  (mapcar #'car
+          (sort (mapcar #'(lambda (x) (cons x (funcall accessor x))) sequence)
+                #'(lambda (x y) (funcall predicate (cdr x) (cdr y))))))
+
 
 (defvar sort-columns-subprocess t)
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]