aboutsummaryrefslogtreecommitdiffstats
path: root/src
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 /src
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.
Diffstat (limited to 'src')
-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
5 files changed, 93 insertions, 24 deletions
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}