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 /src | |
| 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.
Diffstat (limited to 'src')
| -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 |
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; |
| @@ -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 | } |