Skip to content

Commit f41a26f

Browse files
committed
Add sort_by_cached_key method
1 parent bdcc6f9 commit f41a26f

File tree

2 files changed

+56
-14
lines changed

2 files changed

+56
-14
lines changed

src/liballoc/slice.rs

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,33 +1301,76 @@ impl<T> [T] {
13011301
merge_sort(self, |a, b| compare(a, b) == Less);
13021302
}
13031303

1304+
/// Sorts the slice with a key extraction function.
1305+
///
1306+
/// This sort is stable (i.e. does not reorder equal elements) and `O(m n log m n)`
1307+
/// worst-case, where the key function is `O(m)`.
1308+
///
1309+
/// For expensive key functions (e.g. functions that are not simple property accesses or
1310+
/// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be
1311+
/// significantly faster, as it does not recompute element keys.
1312+
///
1313+
/// When applicable, unstable sorting is preferred because it is generally faster than stable
1314+
/// sorting and it doesn't allocate auxiliary memory.
1315+
/// See [`sort_unstable_by_key`](#method.sort_unstable_by_key).
1316+
///
1317+
/// # Current implementation
1318+
///
1319+
/// The current algorithm is an adaptive, iterative merge sort inspired by
1320+
/// [timsort](https://en.wikipedia.org/wiki/Timsort).
1321+
/// It is designed to be very fast in cases where the slice is nearly sorted, or consists of
1322+
/// two or more sorted sequences concatenated one after another.
1323+
///
1324+
/// Also, it allocates temporary storage half the size of `self`, but for short slices a
1325+
/// non-allocating insertion sort is used instead.
1326+
///
1327+
/// # Examples
1328+
///
1329+
/// ```
1330+
/// let mut v = [-5i32, 4, 1, -3, 2];
1331+
///
1332+
/// v.sort_by_key(|k| k.abs());
1333+
/// assert!(v == [1, 2, -3, 4, -5]);
1334+
/// ```
1335+
#[stable(feature = "slice_sort_by_key", since = "1.7.0")]
1336+
#[inline]
1337+
pub fn sort_by_key<K, F>(&mut self, mut f: F)
1338+
where F: FnMut(&T) -> K, K: Ord
1339+
{
1340+
merge_sort(self, |a, b| f(a).lt(&f(b)));
1341+
}
1342+
13041343
/// Sorts the slice with a key extraction function.
13051344
///
13061345
/// During sorting, the key function is called only once per element.
13071346
///
13081347
/// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)`
13091348
/// worst-case, where the key function is `O(m)`.
13101349
///
1350+
/// For simple key functions (e.g. functions that are property accesses or
1351+
/// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be
1352+
/// faster.
1353+
///
13111354
/// # Current implementation
13121355
///
13131356
/// The current algorithm is an adaptive, iterative merge sort inspired by
13141357
/// [timsort](https://en.wikipedia.org/wiki/Timsort).
13151358
/// It is designed to be very fast in cases where the slice is nearly sorted, or consists of
13161359
/// two or more sorted sequences concatenated one after another.
13171360
///
1318-
/// The algorithm allocates temporary storage in a `Vec<(K, usize)` the length of the slice.
1361+
/// The algorithm allocates temporary storage in a `Vec<(K, usize)>` the length of the slice.
13191362
///
13201363
/// # Examples
13211364
///
13221365
/// ```
13231366
/// let mut v = [-5i32, 4, 1, -3, 2];
13241367
///
1325-
/// v.sort_by_key(|k| k.abs());
1368+
/// v.sort_by_cached_key(|k| k.abs());
13261369
/// assert!(v == [1, 2, -3, 4, -5]);
13271370
/// ```
1328-
#[stable(feature = "slice_sort_by_key", since = "1.7.0")]
1371+
#[unstable(feature = "slice_sort_by_uncached_key", issue = "34447")]
13291372
#[inline]
1330-
pub fn sort_by_key<K, F>(&mut self, f: F)
1373+
pub fn sort_by_cached_key<K, F>(&mut self, f: F)
13311374
where F: FnMut(&T) -> K, K: Ord
13321375
{
13331376
// Helper macro for indexing our vector by the smallest possible type, to reduce allocation.
@@ -1432,9 +1475,6 @@ impl<T> [T] {
14321475
/// Sorts the slice with a key extraction function, but may not preserve the order of equal
14331476
/// elements.
14341477
///
1435-
/// Note that, currently, the key function for [`sort_unstable_by_key`] is called multiple times
1436-
/// per element, unlike [`sort_by_key`].
1437-
///
14381478
/// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate),
14391479
/// and `O(m n log m n)` worst-case, where the key function is `O(m)`.
14401480
///
@@ -1446,8 +1486,9 @@ impl<T> [T] {
14461486
/// randomization to avoid degenerate cases, but with a fixed seed to always provide
14471487
/// deterministic behavior.
14481488
///
1449-
/// Due to its key calling strategy, [`sort_unstable_by_key`] is likely to be slower than
1450-
/// [`sort_by_key`] in cases where the key function is expensive.
1489+
/// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key)
1490+
/// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_uncached_key) in
1491+
/// cases where the key function is expensive.
14511492
///
14521493
/// # Examples
14531494
///
@@ -1458,8 +1499,6 @@ impl<T> [T] {
14581499
/// assert!(v == [1, 2, -3, 4, -5]);
14591500
/// ```
14601501
///
1461-
/// [`sort_by_key`]: #method.sort_by_key
1462-
/// [`sort_unstable_by_key`]: #method.sort_unstable_by_key
14631502
/// [pdqsort]: https://github.com/orlp/pdqsort
14641503
#[stable(feature = "sort_unstable", since = "1.20.0")]
14651504
#[inline]

src/liballoc/tests/slice.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,12 @@ fn test_sort() {
426426
assert!(v.windows(2).all(|w| w[0] >= w[1]));
427427

428428
// Sort in lexicographic order.
429-
let mut v = orig.clone();
430-
v.sort_by_key(|x| x.to_string());
431-
assert!(v.windows(2).all(|w| w[0].to_string() <= w[1].to_string()));
429+
let mut v1 = orig.clone();
430+
let mut v2 = orig.clone();
431+
v1.sort_by_key(|x| x.to_string());
432+
v2.sort_by_cached_key(|x| x.to_string());
433+
assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string()));
434+
assert!(v1 == v2);
432435

433436
// Sort with many pre-sorted runs.
434437
let mut v = orig.clone();

0 commit comments

Comments
 (0)