aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2024-03-19 13:03:47 +0100
committerMattias EngdegÄrd2024-03-29 11:39:38 +0100
commitae5f2c02bd2fc269e2cc32c8039d95fbf4225e69 (patch)
treea4c4b2d9cb7288524b7946e0f3263dca4357fd9c
parenta52f1121a3589af8f89828e04d66f1215c361bcf (diff)
downloademacs-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.texi131
-rw-r--r--etc/NEWS25
-rw-r--r--src/dired.c2
-rw-r--r--src/fns.c92
-rw-r--r--src/lisp.h3
-rw-r--r--src/pdumper.c6
-rw-r--r--src/sort.c14
-rw-r--r--test/src/fns-tests.el43
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
357This function sorts @var{sequence} stably. Note that this function doesn't work 357This function sorts @var{sequence}, which must be a list or vector, and
358for all sequences; it may be used only for lists and vectors. If @var{sequence} 358returns a sorted sequence of the same type.
359is a list, it is modified destructively. This functions returns the sorted 359The 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 360their relative order. It takes the following optional keyword arguments:
361one in which elements with equal sort keys maintain their relative order before
362and after the sort. Stability is important when successive sorts are used to
363order elements according to different criteria.
364 361
365The argument @var{predicate} must be a function that accepts two 362@table @asis
366arguments. It is called with two elements of @var{sequence}. To get an 363@item :key @var{keyfunc}
367increasing order sort, the @var{predicate} should return non-@code{nil} if the 364Use @var{keyfunc}, a function that takes a single element from
368first 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
366comparison. If this argument is absent or if @var{keyfunc} is
367@code{nil} then @code{identity} is assumed; that is, the elements
368themselves are used as sorting keys.
369
370@item :lessp @var{predicate}
371Use @var{predicate} to order the keys. @var{predicate} is a function
372that takes two sort keys as arguments and returns non-@code{nil} if the
373first should come before the second. If this argument is absent or
374@var{predicate} is @code{nil}, then @code{value<} is used, which
375is applicable to many different Lisp types and generally sorts in
376ascending order (@pxref{definition of value<}).
377
378For consistency, any predicate must obey the following rules:
379@itemize @bullet
380@item
381It must be @dfn{antisymmetric}: it cannot both order @var{a} before
382@var{b} and @var{b} before @var{a}.
383@item
384It 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
370The comparison function @var{predicate} must give reliable results for 388@item :reverse @var{flag}
371any given pair of arguments, at least within a single call to 389If @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 390the default @code{:lessp} predicate this means sorting in descending order.
373less 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}
375is less than @var{c}, then @var{a} must be less than @var{c}. If you
376use a comparison function which does not meet these requirements, the
377result of @code{sort} is unpredictable.
378 391
379The destructive aspect of @code{sort} for lists is that it reuses the 392@item :in-place @var{flag}
380cons cells forming @var{sequence} by changing their contents, possibly 393If @var{flag} is non-@code{nil}, then @var{sequence} is sorted in-place
381rearranging them in a different order. This means that the value of 394(destructively) and returned. If @code{nil}, or if this argument is not
382the input list is undefined after sorting; only the list returned by 395given, a sorted copy of the input is returned and @var{sequence} itself
383@code{sort} has a well-defined value. Example: 396remains unmodified. In-place sorting is slightly faster, but the
397original sequence is lost.
398@end table
399
400If the default behaviour is not suitable for your needs, it is usually
401easier 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
394Most often we store the result back into the variable that held the 410You can sort the strings by length instead by supplying a different key
395original list: 411function:
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
401If you wish to make a sorted copy without destroying the original, 418Note how strings of the same length keep their original order, thanks to
402copy it first and then sort: 419the sorting stability. Now suppose you want to sort by length, but use
420the string contents to break ties. The easiest way is to specify a key
421function that transforms an element to a value that is sorted this way.
422Since @code{value<} orders compound objects (conses, lists,
423vectors 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
411nums
412 @result{} (2 1 4 3 0)
413@end group
414@end example 428@end example
415 429
416For the better understanding of what stable sort is, consider the following 430because @code{(3 . "six")} is ordered before @code{(3 . "two")} and so on.
417vector example. After sorting, all items whose @code{car} is 8 are grouped 431
418at the beginning of @code{vector}, but their relative order is preserved. 432For compatibility with old versions of Emacs, the @code{sort} function
419All items whose @code{car} is 9 are grouped at the end of @code{vector}, 433can also be called using the fixed two-argument form
420but 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
439where @var{predicate} is the @code{:lessp} argument. When using this
440form, 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
442This function returns non-@code{nil} if @var{a} comes before @var{b} in 447This function returns non-@code{nil} if @var{a} comes before @var{b} in
443the standard sorting order; this means that it returns @code{nil} when 448the standard sorting order; this means that it returns @code{nil} when
diff --git a/etc/NEWS b/etc/NEWS
index 73ffff9f2d3..4018df1fecb 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1770,6 +1770,31 @@ lexicographically.
1770It is intended as a convenient ordering predicate for sorting, and is 1770It is intended as a convenient ordering predicate for sorting, and is
1771likely to be faster than hand-written Lisp functions. 1771likely to be faster than hand-written Lisp functions.
1772 1772
1773+++
1774** New 'sort' arguments and features.
1775The 'sort' function can now be called using the signature
1776
1777 (sort SEQ &rest KEYWORD-ARGUMENTS)
1778
1779where 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
1784sort a copy of the input.
1785
1786The new signature is less error-prone and reduces the need to write
1787ordering predicates by hand. We recommend that you use the ':key'
1788argument instead of ':lessp' unless a suitable ordering predicate is
1789already 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
1793sorts by the return value of 'age', then by 'size', then by 'cost'.
1794
1795The old signature, '(sort SEQ PREDICATE)', can still be used and sorts
1796its input in-place as before.
1797
1773** New function 'sort-on'. 1798** New function 'sort-on'.
1774This function implements the Schwartzian transform, and is appropriate 1799This function implements the Schwartzian transform, and is appropriate
1775for sorting lists when the computation of the sort key of a list 1800for 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;
diff --git a/src/fns.c b/src/fns.c
index a3ef99f67a8..7eacf99cbba 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -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
2355static Lisp_Object 2355static Lisp_Object
2356sort_list (Lisp_Object list, Lisp_Object predicate, Lisp_Object keyfunc) 2356sort_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
2390static void 2391static void
2391sort_vector (Lisp_Object vector, Lisp_Object predicate, Lisp_Object keyfunc) 2392sort_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
2400DEFUN ("sort", Fsort, Ssort, 2, 2, 0, 2402DEFUN ("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.
2402Returns the sorted sequence. SEQ should be a list or vector. SEQ is 2404SEQ should be a list or vector.
2403modified by side effects. PREDICATE is called with two elements of 2405Optional arguments are specified as keyword/argument pairs. The following
2404SEQ, and should return non-nil if the first element should sort before 2406arguments are defined:
2405the 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
2424For compatibility, the calling convention (sort SEQ LESSP) can also be used;
2425in this case, sorting is always done in-place.
2426
2427usage: (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);
4299extern void mark_fns (void); 4299extern void mark_fns (void);
4300 4300
4301/* Defined in sort.c */ 4301/* Defined in sort.c */
4302extern void tim_sort (Lisp_Object, Lisp_Object, Lisp_Object *, const ptrdiff_t); 4302extern 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. */
4305verify (FLT_RADIX == 2 || FLT_RADIX == 16); 4306verify (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
4057dump_do_fixups (struct dump_context *ctx) 4057dump_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. */
1077void 1077void
1078tim_sort (Lisp_Object predicate, Lisp_Object keyfunc, 1078tim_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 ()