- Notifications
You must be signed in to change notification settings - Fork 14k
Add slice::sort_by_cached_key as a memoised sort_by_key #48639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ea6a1bd 670e69e b8452cc 9fbee35 21fde09 7dcfc07 bdcc6f9 f41a26f b430cba ca3bed0 9896b38 81edd17 785e3c3 eca1e18 9c7b69e File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -1301,33 +1301,76 @@ impl<T> [T] { | |
| merge_sort(self, |a, b| compare(a, b) == Less); | ||
| } | ||
| | ||
| /// Sorts the slice with a key extraction function. | ||
| /// | ||
| /// This sort is stable (i.e. does not reorder equal elements) and `O(m n log m n)` | ||
| /// worst-case, where the key function is `O(m)`. | ||
| /// | ||
| /// For expensive key functions (e.g. functions that are not simple property accesses or | ||
| /// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be | ||
| /// significantly faster, as it does not recompute element keys. | ||
| ||
| /// | ||
| /// When applicable, unstable sorting is preferred because it is generally faster than stable | ||
| /// sorting and it doesn't allocate auxiliary memory. | ||
| /// See [`sort_unstable_by_key`](#method.sort_unstable_by_key). | ||
| /// | ||
| /// # Current implementation | ||
| /// | ||
| /// The current algorithm is an adaptive, iterative merge sort inspired by | ||
| /// [timsort](https://en.wikipedia.org/wiki/Timsort). | ||
| /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of | ||
| /// two or more sorted sequences concatenated one after another. | ||
| /// | ||
| /// Also, it allocates temporary storage half the size of `self`, but for short slices a | ||
| /// non-allocating insertion sort is used instead. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// let mut v = [-5i32, 4, 1, -3, 2]; | ||
| /// | ||
| /// v.sort_by_key(|k| k.abs()); | ||
| /// assert!(v == [1, 2, -3, 4, -5]); | ||
| /// ``` | ||
| #[stable(feature = "slice_sort_by_key", since = "1.7.0")] | ||
| #[inline] | ||
| pub fn sort_by_key<K, F>(&mut self, mut f: F) | ||
| where F: FnMut(&T) -> K, K: Ord | ||
| { | ||
| merge_sort(self, |a, b| f(a).lt(&f(b))); | ||
| } | ||
| | ||
| /// Sorts the slice with a key extraction function. | ||
| /// | ||
| /// During sorting, the key function is called only once per element. | ||
| /// | ||
| /// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)` | ||
| /// worst-case, where the key function is `O(m)`. | ||
| /// | ||
| /// For simple key functions (e.g. functions that are property accesses or | ||
| /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be | ||
| /// faster. | ||
| /// | ||
| /// # Current implementation | ||
| /// | ||
| /// The current algorithm is an adaptive, iterative merge sort inspired by | ||
| ||
| /// [timsort](https://en.wikipedia.org/wiki/Timsort). | ||
| /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of | ||
| /// two or more sorted sequences concatenated one after another. | ||
| /// | ||
| /// The algorithm allocates temporary storage in a `Vec<(K, usize)` the length of the slice. | ||
| /// The algorithm allocates temporary storage in a `Vec<(K, usize)>` the length of the slice. | ||
| ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// let mut v = [-5i32, 4, 1, -3, 2]; | ||
| /// | ||
| /// v.sort_by_key(|k| k.abs()); | ||
| /// v.sort_by_cached_key(|k| k.abs()); | ||
| ||
| /// assert!(v == [1, 2, -3, 4, -5]); | ||
| /// ``` | ||
| #[stable(feature = "slice_sort_by_key", since = "1.7.0")] | ||
| #[unstable(feature = "slice_sort_by_uncached_key", issue = "34447")] | ||
| #[inline] | ||
| pub fn sort_by_key<K, F>(&mut self, f: F) | ||
| pub fn sort_by_cached_key<K, F>(&mut self, f: F) | ||
| where F: FnMut(&T) -> K, K: Ord | ||
| { | ||
| // Helper macro for indexing our vector by the smallest possible type, to reduce allocation. | ||
| | @@ -1432,9 +1475,6 @@ impl<T> [T] { | |
| /// Sorts the slice with a key extraction function, but may not preserve the order of equal | ||
| /// elements. | ||
| /// | ||
| /// Note that, currently, the key function for [`sort_unstable_by_key`] is called multiple times | ||
| /// per element, unlike [`sort_by_key`]. | ||
| /// | ||
| /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), | ||
| /// and `O(m n log m n)` worst-case, where the key function is `O(m)`. | ||
| ||
| /// | ||
| | @@ -1446,8 +1486,9 @@ impl<T> [T] { | |
| /// randomization to avoid degenerate cases, but with a fixed seed to always provide | ||
| /// deterministic behavior. | ||
| /// | ||
| /// Due to its key calling strategy, [`sort_unstable_by_key`] is likely to be slower than | ||
| /// [`sort_by_key`] in cases where the key function is expensive. | ||
| /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) | ||
| /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_uncached_key) in | ||
| /// cases where the key function is expensive. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| | @@ -1458,8 +1499,6 @@ impl<T> [T] { | |
| /// assert!(v == [1, 2, -3, 4, -5]); | ||
| /// ``` | ||
| /// | ||
| /// [`sort_by_key`]: #method.sort_by_key | ||
| /// [`sort_unstable_by_key`]: #method.sort_unstable_by_key | ||
| /// [pdqsort]: https://github.com/orlp/pdqsort | ||
| #[stable(feature = "sort_unstable", since = "1.20.0")] | ||
| #[inline] | ||
| | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -426,9 +426,12 @@ fn test_sort() { | |
| assert!(v.windows(2).all(|w| w[0] >= w[1])); | ||
| | ||
| // Sort in lexicographic order. | ||
| let mut v = orig.clone(); | ||
| v.sort_by_key(|x| x.to_string()); | ||
| assert!(v.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); | ||
| let mut v1 = orig.clone(); | ||
| let mut v2 = orig.clone(); | ||
| v1.sort_by_key(|x| x.to_string()); | ||
| v2.sort_by_cached_key(|x| x.to_string()); | ||
| assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); | ||
| assert!(v1 == v2); | ||
| | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since Contributor Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! | ||
| // Sort with many pre-sorted runs. | ||
| let mut v = orig.clone(); | ||
| | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please write it as
O(m n log(m n))to avoid ambiguity.