5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
- use std:: fmt;
9
8
use std:: marker:: PhantomData ;
9
+ use std:: { cmp, fmt} ;
10
10
11
11
use crate :: builtin:: * ;
12
12
use crate :: meta;
@@ -612,37 +612,80 @@ impl<T: ArrayElement> Array<T> {
612
612
}
613
613
}
614
614
615
- /// Finds the index of an existing value in a sorted array using binary search.
616
- /// Equivalent of `bsearch` in GDScript.
615
+ /// Finds the index of a value in a sorted array using binary search.
617
616
///
618
- /// If the value is not present in the array, returns the insertion index that
619
- /// would maintain sorting order.
617
+ /// If the value is not present in the array, returns the insertion index that would maintain sorting order.
620
618
///
621
- /// Calling `bsearch` on an unsorted array results in unspecified behavior.
619
+ /// Calling `bsearch` on an unsorted array results in unspecified behavior. Consider using `sort()` to ensure the sorting
620
+ /// order is compatible with your callable's ordering.
622
621
pub fn bsearch ( & self , value : impl AsArg < T > ) -> usize {
623
622
meta:: arg_into_ref!( value: T ) ;
624
623
625
624
to_usize ( self . as_inner ( ) . bsearch ( & value. to_variant ( ) , true ) )
626
625
}
627
626
628
- /// Finds the index of an existing value in a sorted array using binary search.
629
- /// Equivalent of `bsearch_custom` in GDScript.
627
+ /// Finds the index of a value in a sorted array using binary search, with type-safe custom predicate.
630
628
///
631
- /// Takes a `Callable` and uses the return value of it to perform binary search.
629
+ /// The comparator function should return an ordering that indicates whether its argument is `Less`, `Equal` or `Greater` the desired value.
630
+ /// For example, for an ascending-ordered array, a simple predicate searching for a constant value would be `|elem| elem.cmp(&4)`.
631
+ /// See also [`slice::binary_search_by()`].
632
632
///
633
- /// If the value is not present in the array , returns the insertion index that
634
- /// would maintain sorting order.
633
+ /// If the value is found, returns `Ok(index)` with its index. Otherwise , returns `Err(index)`, where `index` is the insertion index
634
+ /// that would maintain sorting order.
635
635
///
636
- /// Calling `bsearch_custom` on an unsorted array results in unspecified behavior.
636
+ /// Calling `bsearch_by` on an unsorted array results in unspecified behavior. Consider using [`sort_by()`] to ensure
637
+ /// the sorting order is compatible with your callable's ordering.
638
+ #[ cfg( since_api = "4.2" ) ]
639
+ pub fn bsearch_by < F > ( & self , mut func : F ) -> Result < usize , usize >
640
+ where
641
+ F : FnMut ( & T ) -> cmp:: Ordering + ' static ,
642
+ {
643
+ // Early exit; later code relies on index 0 being present.
644
+ if self . is_empty ( ) {
645
+ return Err ( 0 ) ;
646
+ }
647
+
648
+ // We need one dummy element of type T, because Godot's bsearch_custom() checks types (so Variant::nil() can't be passed).
649
+ // Optimization: roundtrip Variant -> T -> Variant could be avoided, but anyone needing speed would use Rust binary search...
650
+ let ignored_value = self . at ( 0 ) ;
651
+ let ignored_value = <T as ParamType >:: owned_to_arg ( ignored_value) ;
652
+
653
+ let godot_comparator = |args : & [ & Variant ] | {
654
+ let value = T :: from_variant ( args[ 0 ] ) ;
655
+ let is_less = matches ! ( func( & value) , cmp:: Ordering :: Less ) ;
656
+
657
+ Ok ( is_less. to_variant ( ) )
658
+ } ;
659
+
660
+ let debug_name = std:: any:: type_name :: < F > ( ) ;
661
+ let index = Callable :: with_scoped_fn ( debug_name, godot_comparator, |pred| {
662
+ self . bsearch_custom ( ignored_value, pred)
663
+ } ) ;
664
+
665
+ if let Some ( value_at_index) = self . get ( index) {
666
+ if func ( & value_at_index) == cmp:: Ordering :: Equal {
667
+ return Ok ( index) ;
668
+ }
669
+ }
670
+
671
+ Err ( index)
672
+ }
673
+
674
+ /// Finds the index of a value in a sorted array using binary search, with `Callable` custom predicate.
675
+ ///
676
+ /// The callable `pred` takes two elements `(a, b)` and should return if `a < b` (strictly less).
677
+ /// For a type-safe version, check out [`bsearch_by()`][Self::bsearch_by].
637
678
///
638
- /// Consider using `sort_custom()` to ensure the sorting order is compatible with
639
- /// your callable's ordering
640
- pub fn bsearch_custom ( & self , value : impl AsArg < T > , func : & Callable ) -> usize {
679
+ /// If the value is not present in the array, returns the insertion index that would maintain sorting order.
680
+ ///
681
+ /// Calling `bsearch_custom` on an unsorted array results in unspecified behavior. Consider using `sort_custom()` to ensure
682
+ /// the sorting order is compatible with your callable's ordering.
683
+ pub fn bsearch_custom ( & self , value : impl AsArg < T > , pred : & Callable ) -> usize {
641
684
meta:: arg_into_ref!( value: T ) ;
642
685
643
686
to_usize (
644
687
self . as_inner ( )
645
- . bsearch_custom ( & value. to_variant ( ) , func , true ) ,
688
+ . bsearch_custom ( & value. to_variant ( ) , pred , true ) ,
646
689
)
647
690
}
648
691
@@ -654,20 +697,55 @@ impl<T: ArrayElement> Array<T> {
654
697
655
698
/// Sorts the array.
656
699
///
657
- /// Note: The sorting algorithm used is not [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
658
- /// This means that values considered equal may have their order changed when using `sort_unstable`.
700
+ /// The sorting algorithm used is not [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
701
+ /// This means that values considered equal may have their order changed when using `sort_unstable`. For most variant types,
702
+ /// this distinction should not matter though.
703
+ ///
704
+ /// _Godot equivalent: `Array.sort()`_
659
705
#[ doc( alias = "sort" ) ]
660
706
pub fn sort_unstable ( & mut self ) {
661
707
// SAFETY: We do not write any values that don't already exist in the array, so all values have the correct type.
662
708
unsafe { self . as_inner_mut ( ) } . sort ( ) ;
663
709
}
664
710
665
- /// Sorts the array.
711
+ /// Sorts the array, using a type-safe comparator.
712
+ ///
713
+ /// The predicate expects two parameters `(a, b)` and should return an ordering relation. For example, simple ascending ordering of the
714
+ /// elements themselves would be achieved with `|a, b| a.cmp(b)`.
715
+ ///
716
+ /// The sorting algorithm used is not [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
717
+ /// This means that values considered equal may have their order changed when using `sort_unstable_by`. For most variant types,
718
+ /// this distinction should not matter though.
719
+ #[ cfg( since_api = "4.2" ) ]
720
+ pub fn sort_unstable_by < F > ( & mut self , mut func : F )
721
+ where
722
+ F : FnMut ( & T , & T ) -> cmp:: Ordering ,
723
+ {
724
+ let godot_comparator = |args : & [ & Variant ] | {
725
+ let lhs = T :: from_variant ( args[ 0 ] ) ;
726
+ let rhs = T :: from_variant ( args[ 1 ] ) ;
727
+ let is_less = matches ! ( func( & lhs, & rhs) , cmp:: Ordering :: Less ) ;
728
+
729
+ Ok ( is_less. to_variant ( ) )
730
+ } ;
731
+
732
+ let debug_name = std:: any:: type_name :: < F > ( ) ;
733
+ Callable :: with_scoped_fn ( debug_name, godot_comparator, |pred| {
734
+ self . sort_unstable_custom ( pred)
735
+ } ) ;
736
+ }
737
+
738
+ /// Sorts the array, using type-unsafe `Callable` comparator.
739
+ ///
740
+ /// For a type-safe variant of this method, use [`sort_unstable_by()`][Self::sort_unstable_by].
741
+ ///
742
+ /// The callable expects two parameters `(lhs, rhs)` and should return a bool `lhs < rhs`.
666
743
///
667
- /// Uses the provided `Callable` to determine ordering.
744
+ /// The sorting algorithm used is not [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
745
+ /// This means that values considered equal may have their order changed when using `sort_unstable_custom`.For most variant types,
746
+ /// this distinction should not matter though.
668
747
///
669
- /// Note: The sorting algorithm used is not [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
670
- /// This means that values considered equal may have their order changed when using `sort_unstable_custom`.
748
+ /// _Godot equivalent: `Array.sort_custom()`_
671
749
#[ doc( alias = "sort_custom" ) ]
672
750
pub fn sort_unstable_custom ( & mut self , func : & Callable ) {
673
751
// SAFETY: We do not write any values that don't already exist in the array, so all values have the correct type.
0 commit comments