diff options
| author | Mattias EngdegÄrd | 2024-03-19 13:03:47 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-03-29 11:39:38 +0100 |
| commit | ae5f2c02bd2fc269e2cc32c8039d95fbf4225e69 (patch) | |
| tree | a4c4b2d9cb7288524b7946e0f3263dca4357fd9c | |
| parent | a52f1121a3589af8f89828e04d66f1215c361bcf (diff) | |
| download | emacs-ae5f2c02bd2fc269e2cc32c8039d95fbf4225e69.tar.gz emacs-ae5f2c02bd2fc269e2cc32c8039d95fbf4225e69.zip | |
New `sort` keyword arguments (bug#69709)
Add the :key, :lessp, :reverse and :in-place keyword arguments.
The old calling style remains available and is unchanged.
* src/fns.c (sort_list, sort_vector, Fsort):
* src/sort.c (tim_sort):
Add keyword arguments with associated new features.
All callers of Fsort adapted.
* test/src/fns-tests.el (fns-tests--shuffle-vector, fns-tests-sort-kw):
New test.
* doc/lispref/sequences.texi (Sequence Functions): Update manual.
* etc/NEWS: Announce.
| -rw-r--r-- | doc/lispref/sequences.texi | 131 | ||||
| -rw-r--r-- | etc/NEWS | 25 | ||||
| -rw-r--r-- | src/dired.c | 2 | ||||
| -rw-r--r-- | src/fns.c | 92 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/pdumper.c | 6 | ||||
| -rw-r--r-- | src/sort.c | 14 | ||||
| -rw-r--r-- | test/src/fns-tests.el | 43 |
8 files changed, 229 insertions, 87 deletions
diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 5bdf71fe02e..6322f17e77b 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi | |||
| @@ -350,94 +350,99 @@ encouraged to treat strings as immutable even when they are mutable. | |||
| 350 | 350 | ||
| 351 | @end defun | 351 | @end defun |
| 352 | 352 | ||
| 353 | @defun sort sequence predicate | 353 | @defun sort sequence &rest keyword-args |
| 354 | @cindex stable sort | 354 | @cindex stable sort |
| 355 | @cindex sorting lists | 355 | @cindex sorting lists |
| 356 | @cindex sorting vectors | 356 | @cindex sorting vectors |
| 357 | This function sorts @var{sequence} stably. Note that this function doesn't work | 357 | This function sorts @var{sequence}, which must be a list or vector, and |
| 358 | for all sequences; it may be used only for lists and vectors. If @var{sequence} | 358 | returns a sorted sequence of the same type. |
| 359 | is a list, it is modified destructively. This functions returns the sorted | 359 | The sort is stable, which means that elements with equal sort keys maintain |
| 360 | @var{sequence} and compares elements using @var{predicate}. A stable sort is | 360 | their relative order. It takes the following optional keyword arguments: |
| 361 | one in which elements with equal sort keys maintain their relative order before | ||
| 362 | and after the sort. Stability is important when successive sorts are used to | ||
| 363 | order elements according to different criteria. | ||
| 364 | 361 | ||
| 365 | The argument @var{predicate} must be a function that accepts two | 362 | @table @asis |
| 366 | arguments. It is called with two elements of @var{sequence}. To get an | 363 | @item :key @var{keyfunc} |
| 367 | increasing order sort, the @var{predicate} should return non-@code{nil} if the | 364 | Use @var{keyfunc}, a function that takes a single element from |
| 368 | first element is ``less'' than the second, or @code{nil} if not. | 365 | @var{sequence} and returns its key value, to generate the keys used in |
| 366 | comparison. If this argument is absent or if @var{keyfunc} is | ||
| 367 | @code{nil} then @code{identity} is assumed; that is, the elements | ||
| 368 | themselves are used as sorting keys. | ||
| 369 | |||
| 370 | @item :lessp @var{predicate} | ||
| 371 | Use @var{predicate} to order the keys. @var{predicate} is a function | ||
| 372 | that takes two sort keys as arguments and returns non-@code{nil} if the | ||
| 373 | first should come before the second. If this argument is absent or | ||
| 374 | @var{predicate} is @code{nil}, then @code{value<} is used, which | ||
| 375 | is applicable to many different Lisp types and generally sorts in | ||
| 376 | ascending order (@pxref{definition of value<}). | ||
| 377 | |||
| 378 | For consistency, any predicate must obey the following rules: | ||
| 379 | @itemize @bullet | ||
| 380 | @item | ||
| 381 | It must be @dfn{antisymmetric}: it cannot both order @var{a} before | ||
| 382 | @var{b} and @var{b} before @var{a}. | ||
| 383 | @item | ||
| 384 | It must be @dfn{transitive}: if it orders @var{a} before @var{b} and | ||
| 385 | @var{b} before @var{c}, then it must also order @var{a} before @var{c}. | ||
| 386 | @end itemize | ||
| 369 | 387 | ||
| 370 | The comparison function @var{predicate} must give reliable results for | 388 | @item :reverse @var{flag} |
| 371 | any given pair of arguments, at least within a single call to | 389 | If @var{flag} is non-@code{nil}, the sorting order is reversed. With |
| 372 | @code{sort}. It must be @dfn{antisymmetric}; that is, if @var{a} is | 390 | the default @code{:lessp} predicate this means sorting in descending order. |
| 373 | less than @var{b}, @var{b} must not be less than @var{a}. It must be | ||
| 374 | @dfn{transitive}---that is, if @var{a} is less than @var{b}, and @var{b} | ||
| 375 | is less than @var{c}, then @var{a} must be less than @var{c}. If you | ||
| 376 | use a comparison function which does not meet these requirements, the | ||
| 377 | result of @code{sort} is unpredictable. | ||
| 378 | 391 | ||
| 379 | The destructive aspect of @code{sort} for lists is that it reuses the | 392 | @item :in-place @var{flag} |
| 380 | cons cells forming @var{sequence} by changing their contents, possibly | 393 | If @var{flag} is non-@code{nil}, then @var{sequence} is sorted in-place |
| 381 | rearranging them in a different order. This means that the value of | 394 | (destructively) and returned. If @code{nil}, or if this argument is not |
| 382 | the input list is undefined after sorting; only the list returned by | 395 | given, a sorted copy of the input is returned and @var{sequence} itself |
| 383 | @code{sort} has a well-defined value. Example: | 396 | remains unmodified. In-place sorting is slightly faster, but the |
| 397 | original sequence is lost. | ||
| 398 | @end table | ||
| 399 | |||
| 400 | If the default behaviour is not suitable for your needs, it is usually | ||
| 401 | easier and faster to supply a new @code{:key} function than a different | ||
| 402 | @code{:lessp} predicate. For example, consider sorting these strings: | ||
| 384 | 403 | ||
| 385 | @example | 404 | @example |
| 386 | @group | 405 | (setq numbers '("one" "two" "three" "four" "five" "six")) |
| 387 | (setq nums (list 2 1 4 3 0)) | 406 | (sort numbers) |
| 388 | (sort nums #'<) | 407 | @result{} ("five" "four" "one" "six" "three" "two") |
| 389 | @result{} (0 1 2 3 4) | ||
| 390 | ; nums is unpredictable at this point | ||
| 391 | @end group | ||
| 392 | @end example | 408 | @end example |
| 393 | 409 | ||
| 394 | Most often we store the result back into the variable that held the | 410 | You can sort the strings by length instead by supplying a different key |
| 395 | original list: | 411 | function: |
| 396 | 412 | ||
| 397 | @example | 413 | @example |
| 398 | (setq nums (sort nums #'<)) | 414 | (sort numbers :key #'length) |
| 415 | @result{} ("one" "two" "six" "four" "five" "three") | ||
| 399 | @end example | 416 | @end example |
| 400 | 417 | ||
| 401 | If you wish to make a sorted copy without destroying the original, | 418 | Note how strings of the same length keep their original order, thanks to |
| 402 | copy it first and then sort: | 419 | the sorting stability. Now suppose you want to sort by length, but use |
| 420 | the string contents to break ties. The easiest way is to specify a key | ||
| 421 | function that transforms an element to a value that is sorted this way. | ||
| 422 | Since @code{value<} orders compound objects (conses, lists, | ||
| 423 | vectors and records) lexicographically, you could do: | ||
| 403 | 424 | ||
| 404 | @example | 425 | @example |
| 405 | @group | 426 | (sort numbers :key (lambda (x) (cons (length x) x))) |
| 406 | (setq nums (list 2 1 4 3 0)) | 427 | @result{} ("one" "six" "two" "five" "four" "three") |
| 407 | (sort (copy-sequence nums) #'<) | ||
| 408 | @result{} (0 1 2 3 4) | ||
| 409 | @end group | ||
| 410 | @group | ||
| 411 | nums | ||
| 412 | @result{} (2 1 4 3 0) | ||
| 413 | @end group | ||
| 414 | @end example | 428 | @end example |
| 415 | 429 | ||
| 416 | For the better understanding of what stable sort is, consider the following | 430 | because @code{(3 . "six")} is ordered before @code{(3 . "two")} and so on. |
| 417 | vector example. After sorting, all items whose @code{car} is 8 are grouped | 431 | |
| 418 | at the beginning of @code{vector}, but their relative order is preserved. | 432 | For compatibility with old versions of Emacs, the @code{sort} function |
| 419 | All items whose @code{car} is 9 are grouped at the end of @code{vector}, | 433 | can also be called using the fixed two-argument form |
| 420 | but their relative order is also preserved: | ||
| 421 | 434 | ||
| 422 | @example | 435 | @example |
| 423 | @group | 436 | (@code{sort} @var{sequence} @var{predicate}) |
| 424 | (setq | ||
| 425 | vector | ||
| 426 | (vector '(8 . "xxx") '(9 . "aaa") '(8 . "bbb") '(9 . "zzz") | ||
| 427 | '(9 . "ppp") '(8 . "ttt") '(8 . "eee") '(9 . "fff"))) | ||
| 428 | @result{} [(8 . "xxx") (9 . "aaa") (8 . "bbb") (9 . "zzz") | ||
| 429 | (9 . "ppp") (8 . "ttt") (8 . "eee") (9 . "fff")] | ||
| 430 | @end group | ||
| 431 | @group | ||
| 432 | (sort vector (lambda (x y) (< (car x) (car y)))) | ||
| 433 | @result{} [(8 . "xxx") (8 . "bbb") (8 . "ttt") (8 . "eee") | ||
| 434 | (9 . "aaa") (9 . "zzz") (9 . "ppp") (9 . "fff")] | ||
| 435 | @end group | ||
| 436 | @end example | 437 | @end example |
| 438 | |||
| 439 | where @var{predicate} is the @code{:lessp} argument. When using this | ||
| 440 | form, sorting is always done in-place. | ||
| 437 | @end defun | 441 | @end defun |
| 438 | 442 | ||
| 439 | @cindex comparing values | 443 | @cindex comparing values |
| 440 | @cindex standard sorting order | 444 | @cindex standard sorting order |
| 445 | @anchor{definition of value<} | ||
| 441 | @defun value< a b | 446 | @defun value< a b |
| 442 | This function returns non-@code{nil} if @var{a} comes before @var{b} in | 447 | This function returns non-@code{nil} if @var{a} comes before @var{b} in |
| 443 | the standard sorting order; this means that it returns @code{nil} when | 448 | the standard sorting order; this means that it returns @code{nil} when |
| @@ -1770,6 +1770,31 @@ lexicographically. | |||
| 1770 | It is intended as a convenient ordering predicate for sorting, and is | 1770 | It is intended as a convenient ordering predicate for sorting, and is |
| 1771 | likely to be faster than hand-written Lisp functions. | 1771 | likely to be faster than hand-written Lisp functions. |
| 1772 | 1772 | ||
| 1773 | +++ | ||
| 1774 | ** New 'sort' arguments and features. | ||
| 1775 | The 'sort' function can now be called using the signature | ||
| 1776 | |||
| 1777 | (sort SEQ &rest KEYWORD-ARGUMENTS) | ||
| 1778 | |||
| 1779 | where arguments after the first are keyword/value pairs, all optional: | ||
| 1780 | ':key' specifies a function that produces the sorting key from an element, | ||
| 1781 | ':lessp' specifies the ordering predicate, defaulting to 'value<', | ||
| 1782 | ':reverse' is used to reverse the sorting order, | ||
| 1783 | ':in-place is used for in-place sorting, as the default is now to | ||
| 1784 | sort a copy of the input. | ||
| 1785 | |||
| 1786 | The new signature is less error-prone and reduces the need to write | ||
| 1787 | ordering predicates by hand. We recommend that you use the ':key' | ||
| 1788 | argument instead of ':lessp' unless a suitable ordering predicate is | ||
| 1789 | already available. This can also be used for multi-key sorting: | ||
| 1790 | |||
| 1791 | (sort seq :key (lambda (x) (list (age x) (size x) (cost x)))) | ||
| 1792 | |||
| 1793 | sorts by the return value of 'age', then by 'size', then by 'cost'. | ||
| 1794 | |||
| 1795 | The old signature, '(sort SEQ PREDICATE)', can still be used and sorts | ||
| 1796 | its input in-place as before. | ||
| 1797 | |||
| 1773 | ** New function 'sort-on'. | 1798 | ** New function 'sort-on'. |
| 1774 | This function implements the Schwartzian transform, and is appropriate | 1799 | This function implements the Schwartzian transform, and is appropriate |
| 1775 | for sorting lists when the computation of the sort key of a list | 1800 | for sorting lists when the computation of the sort key of a list |
diff --git a/src/dired.c b/src/dired.c index 9a372201ae0..bfbacf70917 100644 --- a/src/dired.c +++ b/src/dired.c | |||
| @@ -351,7 +351,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full, | |||
| 351 | specpdl_ptr = specpdl_ref_to_ptr (count); | 351 | specpdl_ptr = specpdl_ref_to_ptr (count); |
| 352 | 352 | ||
| 353 | if (NILP (nosort)) | 353 | if (NILP (nosort)) |
| 354 | list = Fsort (Fnreverse (list), | 354 | list = CALLN (Fsort, Fnreverse (list), |
| 355 | attrs ? Qfile_attributes_lessp : Qstring_lessp); | 355 | attrs ? Qfile_attributes_lessp : Qstring_lessp); |
| 356 | 356 | ||
| 357 | (void) directory_volatile; | 357 | (void) directory_volatile; |
| @@ -2353,7 +2353,8 @@ See also the function `nreverse', which is used more often. */) | |||
| 2353 | is destructively reused to hold the sorted result. */ | 2353 | is destructively reused to hold the sorted result. */ |
| 2354 | 2354 | ||
| 2355 | static Lisp_Object | 2355 | static Lisp_Object |
| 2356 | sort_list (Lisp_Object list, Lisp_Object predicate, Lisp_Object keyfunc) | 2356 | sort_list (Lisp_Object list, Lisp_Object predicate, Lisp_Object keyfunc, |
| 2357 | bool reverse) | ||
| 2357 | { | 2358 | { |
| 2358 | ptrdiff_t length = list_length (list); | 2359 | ptrdiff_t length = list_length (list); |
| 2359 | if (length < 2) | 2360 | if (length < 2) |
| @@ -2369,7 +2370,7 @@ sort_list (Lisp_Object list, Lisp_Object predicate, Lisp_Object keyfunc) | |||
| 2369 | result[i] = Fcar (tail); | 2370 | result[i] = Fcar (tail); |
| 2370 | tail = XCDR (tail); | 2371 | tail = XCDR (tail); |
| 2371 | } | 2372 | } |
| 2372 | tim_sort (predicate, keyfunc, result, length); | 2373 | tim_sort (predicate, keyfunc, result, length, reverse); |
| 2373 | 2374 | ||
| 2374 | ptrdiff_t i = 0; | 2375 | ptrdiff_t i = 0; |
| 2375 | tail = list; | 2376 | tail = list; |
| @@ -2388,27 +2389,86 @@ sort_list (Lisp_Object list, Lisp_Object predicate, Lisp_Object keyfunc) | |||
| 2388 | algorithm. */ | 2389 | algorithm. */ |
| 2389 | 2390 | ||
| 2390 | static void | 2391 | static void |
| 2391 | sort_vector (Lisp_Object vector, Lisp_Object predicate, Lisp_Object keyfunc) | 2392 | sort_vector (Lisp_Object vector, Lisp_Object predicate, Lisp_Object keyfunc, |
| 2393 | bool reverse) | ||
| 2392 | { | 2394 | { |
| 2393 | ptrdiff_t length = ASIZE (vector); | 2395 | ptrdiff_t length = ASIZE (vector); |
| 2394 | if (length < 2) | 2396 | if (length < 2) |
| 2395 | return; | 2397 | return; |
| 2396 | 2398 | ||
| 2397 | tim_sort (predicate, keyfunc, XVECTOR (vector)->contents, length); | 2399 | tim_sort (predicate, keyfunc, XVECTOR (vector)->contents, length, reverse); |
| 2398 | } | 2400 | } |
| 2399 | 2401 | ||
| 2400 | DEFUN ("sort", Fsort, Ssort, 2, 2, 0, | 2402 | DEFUN ("sort", Fsort, Ssort, 1, MANY, 0, |
| 2401 | doc: /* Sort SEQ, stably, comparing elements using PREDICATE. | 2403 | doc: /* Sort SEQ, stably, and return the sorted sequence. |
| 2402 | Returns the sorted sequence. SEQ should be a list or vector. SEQ is | 2404 | SEQ should be a list or vector. |
| 2403 | modified by side effects. PREDICATE is called with two elements of | 2405 | Optional arguments are specified as keyword/argument pairs. The following |
| 2404 | SEQ, and should return non-nil if the first element should sort before | 2406 | arguments are defined: |
| 2405 | the second. */) | 2407 | |
| 2406 | (Lisp_Object seq, Lisp_Object predicate) | 2408 | :key FUNC -- FUNC is a function that takes a single element from SEQ and |
| 2409 | returns the key value to be used in comparison. If absent or nil, | ||
| 2410 | `identity' is used. | ||
| 2411 | |||
| 2412 | :lessp FUNC -- FUNC is a function that takes two arguments and returns | ||
| 2413 | non-nil if the first element should come before the second. | ||
| 2414 | If absent or nil, `value<' is used. | ||
| 2415 | |||
| 2416 | :reverse BOOL -- if BOOL is non-nil, the sorting order implied by FUNC is | ||
| 2417 | reversed. This does not affect stability: equal elements still retain | ||
| 2418 | their order in the input sequence. | ||
| 2419 | |||
| 2420 | :in-place BOOL -- if BOOL is non-nil, SEQ is sorted in-place and returned. | ||
| 2421 | Otherwise, a sorted copy of SEQ is returned and SEQ remains unmodified; | ||
| 2422 | this is the default. | ||
| 2423 | |||
| 2424 | For compatibility, the calling convention (sort SEQ LESSP) can also be used; | ||
| 2425 | in this case, sorting is always done in-place. | ||
| 2426 | |||
| 2427 | usage: (sort SEQ &key KEY LESSP REVERSE IN-PLACE) */) | ||
| 2428 | (ptrdiff_t nargs, Lisp_Object *args) | ||
| 2407 | { | 2429 | { |
| 2430 | Lisp_Object seq = args[0]; | ||
| 2431 | Lisp_Object key = Qnil; | ||
| 2432 | Lisp_Object lessp = Qnil; | ||
| 2433 | bool inplace = false; | ||
| 2434 | bool reverse = false; | ||
| 2435 | if (nargs == 2) | ||
| 2436 | { | ||
| 2437 | /* old-style invocation without keywords */ | ||
| 2438 | lessp = args[1]; | ||
| 2439 | inplace = true; | ||
| 2440 | } | ||
| 2441 | else if ((nargs & 1) == 0) | ||
| 2442 | error ("Invalid argument list"); | ||
| 2443 | else | ||
| 2444 | for (ptrdiff_t i = 1; i < nargs - 1; i += 2) | ||
| 2445 | { | ||
| 2446 | if (EQ (args[i], QCkey)) | ||
| 2447 | key = args[i + 1]; | ||
| 2448 | else if (EQ (args[i], QClessp)) | ||
| 2449 | lessp = args[i + 1]; | ||
| 2450 | else if (EQ (args[i], QCin_place)) | ||
| 2451 | inplace = !NILP (args[i + 1]); | ||
| 2452 | else if (EQ (args[i], QCreverse)) | ||
| 2453 | reverse = !NILP (args[i + 1]); | ||
| 2454 | else | ||
| 2455 | signal_error ("Invalid keyword argument", args[i]); | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | if (NILP (lessp)) | ||
| 2459 | /* FIXME: normalise it as Qnil instead, and special-case it in tim_sort? | ||
| 2460 | That would remove the funcall overhead for the common case. */ | ||
| 2461 | lessp = Qvaluelt; | ||
| 2462 | |||
| 2463 | /* FIXME: for lists it may be slightly faster to make the copy after | ||
| 2464 | sorting? Measure. */ | ||
| 2465 | if (!inplace) | ||
| 2466 | seq = Fcopy_sequence (seq); | ||
| 2467 | |||
| 2408 | if (CONSP (seq)) | 2468 | if (CONSP (seq)) |
| 2409 | seq = sort_list (seq, predicate, Qnil); | 2469 | seq = sort_list (seq, lessp, key, reverse); |
| 2410 | else if (VECTORP (seq)) | 2470 | else if (VECTORP (seq)) |
| 2411 | sort_vector (seq, predicate, Qnil); | 2471 | sort_vector (seq, lessp, key, reverse); |
| 2412 | else if (!NILP (seq)) | 2472 | else if (!NILP (seq)) |
| 2413 | wrong_type_argument (Qlist_or_vector_p, seq); | 2473 | wrong_type_argument (Qlist_or_vector_p, seq); |
| 2414 | return seq; | 2474 | return seq; |
| @@ -6860,4 +6920,10 @@ For best results this should end in a space. */); | |||
| 6860 | DEFSYM (Qfrom__tty_menu_p, "from--tty-menu-p"); | 6920 | DEFSYM (Qfrom__tty_menu_p, "from--tty-menu-p"); |
| 6861 | DEFSYM (Qyes_or_no_p, "yes-or-no-p"); | 6921 | DEFSYM (Qyes_or_no_p, "yes-or-no-p"); |
| 6862 | DEFSYM (Qy_or_n_p, "y-or-n-p"); | 6922 | DEFSYM (Qy_or_n_p, "y-or-n-p"); |
| 6923 | |||
| 6924 | DEFSYM (QCkey, ":key"); | ||
| 6925 | DEFSYM (QClessp, ":lessp"); | ||
| 6926 | DEFSYM (QCin_place, ":in-place"); | ||
| 6927 | DEFSYM (QCreverse, ":reverse"); | ||
| 6928 | DEFSYM (Qvaluelt, "value<"); | ||
| 6863 | } | 6929 | } |
diff --git a/src/lisp.h b/src/lisp.h index 14c0b8e4d1c..6226ab33244 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4299,7 +4299,8 @@ extern void syms_of_fns (void); | |||
| 4299 | extern void mark_fns (void); | 4299 | extern void mark_fns (void); |
| 4300 | 4300 | ||
| 4301 | /* Defined in sort.c */ | 4301 | /* Defined in sort.c */ |
| 4302 | extern void tim_sort (Lisp_Object, Lisp_Object, Lisp_Object *, const ptrdiff_t); | 4302 | extern void tim_sort (Lisp_Object, Lisp_Object, Lisp_Object *, const ptrdiff_t, |
| 4303 | bool); | ||
| 4303 | 4304 | ||
| 4304 | /* Defined in floatfns.c. */ | 4305 | /* Defined in floatfns.c. */ |
| 4305 | verify (FLT_RADIX == 2 || FLT_RADIX == 16); | 4306 | verify (FLT_RADIX == 2 || FLT_RADIX == 16); |
diff --git a/src/pdumper.c b/src/pdumper.c index c7ebb38dea5..ac8bf6f31f4 100644 --- a/src/pdumper.c +++ b/src/pdumper.c | |||
| @@ -3368,7 +3368,7 @@ dump_sort_copied_objects (struct dump_context *ctx) | |||
| 3368 | file and the copy into Emacs in-order, where prefetch will be | 3368 | file and the copy into Emacs in-order, where prefetch will be |
| 3369 | most effective. */ | 3369 | most effective. */ |
| 3370 | ctx->copied_queue = | 3370 | ctx->copied_queue = |
| 3371 | Fsort (Fnreverse (ctx->copied_queue), | 3371 | CALLN (Fsort, Fnreverse (ctx->copied_queue), |
| 3372 | Qdump_emacs_portable__sort_predicate_copied); | 3372 | Qdump_emacs_portable__sort_predicate_copied); |
| 3373 | } | 3373 | } |
| 3374 | 3374 | ||
| @@ -3935,7 +3935,7 @@ drain_reloc_list (struct dump_context *ctx, | |||
| 3935 | { | 3935 | { |
| 3936 | struct dump_flags old_flags = ctx->flags; | 3936 | struct dump_flags old_flags = ctx->flags; |
| 3937 | ctx->flags.pack_objects = true; | 3937 | ctx->flags.pack_objects = true; |
| 3938 | Lisp_Object relocs = Fsort (Fnreverse (*reloc_list), | 3938 | Lisp_Object relocs = CALLN (Fsort, Fnreverse (*reloc_list), |
| 3939 | Qdump_emacs_portable__sort_predicate); | 3939 | Qdump_emacs_portable__sort_predicate); |
| 3940 | *reloc_list = Qnil; | 3940 | *reloc_list = Qnil; |
| 3941 | dump_align_output (ctx, max (alignof (struct dump_reloc), | 3941 | dump_align_output (ctx, max (alignof (struct dump_reloc), |
| @@ -4057,7 +4057,7 @@ static void | |||
| 4057 | dump_do_fixups (struct dump_context *ctx) | 4057 | dump_do_fixups (struct dump_context *ctx) |
| 4058 | { | 4058 | { |
| 4059 | dump_off saved_offset = ctx->offset; | 4059 | dump_off saved_offset = ctx->offset; |
| 4060 | Lisp_Object fixups = Fsort (Fnreverse (ctx->fixups), | 4060 | Lisp_Object fixups = CALLN (Fsort, Fnreverse (ctx->fixups), |
| 4061 | Qdump_emacs_portable__sort_predicate); | 4061 | Qdump_emacs_portable__sort_predicate); |
| 4062 | Lisp_Object prev_fixup = Qnil; | 4062 | Lisp_Object prev_fixup = Qnil; |
| 4063 | ctx->fixups = Qnil; | 4063 | ctx->fixups = Qnil; |
diff --git a/src/sort.c b/src/sort.c index d91993c8c65..a0f127c35b3 100644 --- a/src/sort.c +++ b/src/sort.c | |||
| @@ -1072,11 +1072,11 @@ resolve_fun (Lisp_Object fun) | |||
| 1072 | } | 1072 | } |
| 1073 | 1073 | ||
| 1074 | /* Sort the array SEQ with LENGTH elements in the order determined by | 1074 | /* Sort the array SEQ with LENGTH elements in the order determined by |
| 1075 | PREDICATE. */ | 1075 | PREDICATE (where Qnil means value<) and KEYFUNC (where Qnil means identity), |
| 1076 | 1076 | optionally reversed. */ | |
| 1077 | void | 1077 | void |
| 1078 | tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, | 1078 | tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, |
| 1079 | Lisp_Object *seq, const ptrdiff_t length) | 1079 | Lisp_Object *seq, const ptrdiff_t length, bool reverse) |
| 1080 | { | 1080 | { |
| 1081 | /* FIXME: optimise for the predicate being value<; at the very | 1081 | /* FIXME: optimise for the predicate being value<; at the very |
| 1082 | least we'd go without the Lisp funcall overhead. */ | 1082 | least we'd go without the Lisp funcall overhead. */ |
| @@ -1091,9 +1091,8 @@ tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, | |||
| 1091 | if (EQ (keyfunc, Qidentity)) | 1091 | if (EQ (keyfunc, Qidentity)) |
| 1092 | keyfunc = Qnil; | 1092 | keyfunc = Qnil; |
| 1093 | 1093 | ||
| 1094 | /* FIXME: consider a built-in reverse sorting flag: we would reverse | 1094 | if (reverse) |
| 1095 | the input in-place here and reverse it back just before | 1095 | reverse_slice (seq, seq + length); /* preserve stability */ |
| 1096 | returning. */ | ||
| 1097 | 1096 | ||
| 1098 | if (NILP (keyfunc)) | 1097 | if (NILP (keyfunc)) |
| 1099 | { | 1098 | { |
| @@ -1159,6 +1158,9 @@ tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, | |||
| 1159 | eassume (ms.pending[0].len == length); | 1158 | eassume (ms.pending[0].len == length); |
| 1160 | lo = ms.pending[0].base; | 1159 | lo = ms.pending[0].base; |
| 1161 | 1160 | ||
| 1161 | if (reverse) | ||
| 1162 | reverse_slice (seq, seq + length); | ||
| 1163 | |||
| 1162 | if (ms.a.keys != ms.temparray || allocated_keys != NULL) | 1164 | if (ms.a.keys != ms.temparray || allocated_keys != NULL) |
| 1163 | unbind_to (ms.count, Qnil); | 1165 | unbind_to (ms.count, Qnil); |
| 1164 | } | 1166 | } |
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el index 844000cdc76..1b13785a9fc 100644 --- a/test/src/fns-tests.el +++ b/test/src/fns-tests.el | |||
| @@ -375,6 +375,49 @@ | |||
| 375 | (should (equal (should-error (sort "cba" #'<) :type 'wrong-type-argument) | 375 | (should (equal (should-error (sort "cba" #'<) :type 'wrong-type-argument) |
| 376 | '(wrong-type-argument list-or-vector-p "cba")))) | 376 | '(wrong-type-argument list-or-vector-p "cba")))) |
| 377 | 377 | ||
| 378 | (defun fns-tests--shuffle-vector (vect) | ||
| 379 | "Shuffle VECT in place." | ||
| 380 | (let ((n (length vect))) | ||
| 381 | (dotimes (i (1- n)) | ||
| 382 | (let* ((j (+ i (random (- n i)))) | ||
| 383 | (vi (aref vect i))) | ||
| 384 | (aset vect i (aref vect j)) | ||
| 385 | (aset vect j vi))))) | ||
| 386 | |||
| 387 | (ert-deftest fns-tests-sort-kw () | ||
| 388 | ;; Test the `sort' keyword calling convention by comparing with | ||
| 389 | ;; the results from using the old (positional) style tested above. | ||
| 390 | (random "my seed") | ||
| 391 | (dolist (size '(0 1 2 3 10 100 1000)) | ||
| 392 | ;; Use a vector with both positive and negative numbers (asymmetric). | ||
| 393 | (let ((numbers (vconcat | ||
| 394 | (number-sequence (- (/ size 3)) (- size 1 (/ size 3)))))) | ||
| 395 | (fns-tests--shuffle-vector numbers) | ||
| 396 | ;; Test both list and vector input. | ||
| 397 | (dolist (input (list (append numbers nil) numbers)) | ||
| 398 | (dolist (in-place '(nil t)) | ||
| 399 | (dolist (reverse '(nil t)) | ||
| 400 | (dolist (key '(nil abs)) | ||
| 401 | (dolist (lessp '(nil >)) | ||
| 402 | (let* ((seq (copy-sequence input)) | ||
| 403 | (res (sort seq :key key :lessp lessp | ||
| 404 | :in-place in-place :reverse reverse)) | ||
| 405 | (pred (or lessp #'value<)) | ||
| 406 | (exp-in (copy-sequence input)) | ||
| 407 | (exp-out | ||
| 408 | (sort (if reverse (reverse exp-in) exp-in) | ||
| 409 | (if key | ||
| 410 | (lambda (a b) | ||
| 411 | (funcall pred | ||
| 412 | (funcall key a) (funcall key b))) | ||
| 413 | pred))) | ||
| 414 | (expected (if reverse (reverse exp-out) exp-out))) | ||
| 415 | (should (equal res expected)) | ||
| 416 | (if in-place | ||
| 417 | (should (eq res seq)) | ||
| 418 | (should-not (and (> size 0) (eq res seq))) | ||
| 419 | (should (equal seq input)))))))))))) | ||
| 420 | |||
| 378 | (defvar w32-collate-ignore-punctuation) | 421 | (defvar w32-collate-ignore-punctuation) |
| 379 | 422 | ||
| 380 | (ert-deftest fns-tests-collate-sort () | 423 | (ert-deftest fns-tests-collate-sort () |