From 0e87d5d17108efa05dd2832483ac2ad8564cb880 Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 26 Nov 2025 00:42:22 +0800 Subject: [PATCH 1/2] Implement partial_sort_unstable for slice Signed-off-by: tison --- library/core/src/slice/mod.rs | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index f03f2045444df..7a3e7201046e8 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3242,6 +3242,77 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } + /// Partially sorts the slice in ascending order **without** preserving the initial order + /// of equal elements. + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable(&mut self, range: R) + where + T: Ord, + R: RangeBounds, + { + self.partial_sort_unstable_by(range, T::cmp); + } + + /// Partially sorts the slice in ascending order with a comparison function, **without** + /// preserving the initial order of equal elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// v.partial_sort_unstable_by(.., |a, b| a.cmp(b)); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); + /// + /// // reverse sorting + /// v.partial_sort_unstable_by(.., |a, b| b.cmp(a)); + /// assert_eq!(v, [4, 2, 1, -3, -5]); + /// ``` + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by(&mut self, range: R, mut compare: F) + where + F: FnMut(&T, &T) -> Ordering, + R: RangeBounds, + { + let len = self.len(); + let Range { start, end } = slice::range(range, ..len); + + if start == end { + // empty range, nothing to do + return; + } + + let index = start; + let (_, _, rest) = + sort::select::partition_at_index(self, index, |a, b| compare(a, b) == Less); + + if start + 2 > end { + // the rest slice is of length 0 or 1, already sorted + return; + } + + let index = end - start - 2; + let (rest, _, _) = + sort::select::partition_at_index(rest, index, |a, b| compare(a, b) == Less); + sort::unstable::sort(rest, &mut |a, b| compare(a, b) == Less); + } + + /// Partially sorts the slice in ascending order with a key extraction function, **without** + /// preserving the initial order of equal elements. + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by_key(&mut self, range: R, mut f: F) + where + F: FnMut(&T) -> K, + K: Ord, + R: RangeBounds, + { + self.partial_sort_unstable_by(range, |a, b| f(a).cmp(&f(b))); + } + /// Reorders the slice such that the element at `index` is at a sort-order position. All /// elements before `index` will be `<=` to this value, and all elements after will be `>=` to /// it. From f9a09e0d8980c25d2a2b11c2253b0b81da779380 Mon Sep 17 00:00:00 2001 From: tison Date: Wed, 26 Nov 2025 01:52:57 +0800 Subject: [PATCH 2/2] Better document for partial_sort_unstable Signed-off-by: tison --- library/core/src/slice/mod.rs | 155 ++++++++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 8 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 7a3e7201046e8..7da2d293c413a 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3242,8 +3242,64 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } - /// Partially sorts the slice in ascending order **without** preserving the initial order - /// of equal elements. + /// Partially sorts the slice in ascending order **without** preserving the initial order of equal elements. + /// + /// Upon completion, the elements in the specified `range` will be the elements of the slice + /// as if the whole slice were sorted in ascending order. + /// + /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not + /// allocate), and *O*(*n* + *k* \* log(*k*)) worst-case. + /// + /// If the implementation of [`Ord`] for `T` does not implement a [total order], the function + /// may panic; even if the function exits normally, the resulting order of elements in the slice + /// is unspecified. See also the note on panicking below. + /// + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. + /// + /// All original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if the implementation of [`Ord`] for `T` panics. + /// + /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require + /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the + /// reflexivity requirement of [`Ord`]. By using an alternative comparison function with + /// `slice::partial_sort_unstable_by` such as [`f32::total_cmp`] or [`f64::total_cmp`] that defines a + /// [total order] users can sort slices containing floating-point values. Alternatively, if all + /// values in the slice are guaranteed to be in a subset for which [`PartialOrd::partial_cmp`] + /// forms a [total order], it's possible to sort the slice with `partial_sort_unstable_by(|a, b| + /// a.partial_cmp(b).unwrap())`. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order], or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// + /// // empty range, nothing changed + /// v.partial_sort_unstable(0..0); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable(2..3); + /// assert_eq!(v[2], 1); + /// + /// // partial sort a subrange + /// v.partial_sort_unstable(1..4); + /// assert_eq!(&v[1..4], [-3, 1, 2]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable(..); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); + /// ``` + /// + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] #[inline] pub fn partial_sort_unstable(&mut self, range: R) @@ -3257,19 +3313,53 @@ impl [T] { /// Partially sorts the slice in ascending order with a comparison function, **without** /// preserving the initial order of equal elements. /// + /// Upon completion, the elements in the specified `range` will be the elements of the slice + /// as if the whole slice were sorted in ascending order. + /// + /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not + /// allocate), and *O*(*n* + *k* \* log(*k*)) worst-case. + /// + /// If the comparison function `compare` does not implement a [total order], the function + /// may panic; even if the function exits normally, the resulting order of elements in the slice + /// is unspecified. See also the note on panicking below. + /// + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. + /// + /// All original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if `compare` panics. + /// + /// # Panics + /// + /// May panic if the `compare` does not implement a [total order], or if + /// the `compare` itself panics, or if the specified range is out of bounds. + /// /// # Examples /// /// ``` /// #![feature(slice_partial_sort_unstable)] /// /// let mut v = [4, -5, 1, -3, 2]; - /// v.partial_sort_unstable_by(.., |a, b| a.cmp(b)); - /// assert_eq!(v, [-5, -3, 1, 2, 4]); /// - /// // reverse sorting + /// // empty range, nothing changed + /// v.partial_sort_unstable_by(0..0, |a, b| b.cmp(a)); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by(2..3, |a, b| b.cmp(a)); + /// assert_eq!(v[2], 1); + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by(1..4, |a, b| b.cmp(a)); + /// assert_eq!(&v[1..4], [2, 1, -3]); + /// + /// // partial sort the whole range, same as sort_unstable /// v.partial_sort_unstable_by(.., |a, b| b.cmp(a)); /// assert_eq!(v, [4, 2, 1, -3, -5]); /// ``` + /// + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] #[inline] pub fn partial_sort_unstable_by(&mut self, range: R, mut compare: F) @@ -3280,8 +3370,8 @@ impl [T] { let len = self.len(); let Range { start, end } = slice::range(range, ..len); - if start == end { - // empty range, nothing to do + if start + 1 > end { + // target range is empty, nothing to do return; } @@ -3290,18 +3380,67 @@ impl [T] { sort::select::partition_at_index(self, index, |a, b| compare(a, b) == Less); if start + 2 > end { - // the rest slice is of length 0 or 1, already sorted + // target range is a single element, nothing more to do return; } let index = end - start - 2; let (rest, _, _) = sort::select::partition_at_index(rest, index, |a, b| compare(a, b) == Less); + sort::unstable::sort(rest, &mut |a, b| compare(a, b) == Less); } /// Partially sorts the slice in ascending order with a key extraction function, **without** /// preserving the initial order of equal elements. + /// + /// Upon completion, the elements in the specified `range` will be the elements of the slice + /// as if the whole slice were sorted in ascending order. + /// + /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not + /// allocate), and *O*(*n* + *k* \* log(*k*)) worst-case. + /// + /// If the implementation of [`Ord`] for `K` does not implement a [total order], the function + /// may panic; even if the function exits normally, the resulting order of elements in the slice + /// is unspecified. See also the note on panicking below. + /// + /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor + /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and + /// examples see the [`Ord`] documentation. + /// + /// All original elements will remain in the slice and any possible modifications via interior + /// mutability are observed in the input. Same is true if the implementation of [`Ord`] for `K` panics. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order], or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4i32, -5, 1, -3, 2]; + /// + /// // empty range, nothing changed + /// v.partial_sort_unstable_by_key(0..0, |k| k.abs()); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by_key(2..3, |k| k.abs()); + /// assert_eq!(v[2], -3); + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by_key(1..4, |k| k.abs()); + /// assert_eq!(&v[1..4], [2, -3, 4]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable_by_key(.., |k| k.abs()); + /// assert_eq!(v, [1, 2, -3, 4, -5]); + /// ``` + /// + /// [total order]: https://en.wikipedia.org/wiki/Total_order #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] #[inline] pub fn partial_sort_unstable_by_key(&mut self, range: R, mut f: F)