Skip to content

Commit

Permalink
xapi-stdext-std: add Listext.List.find_first_sorted
Browse files Browse the repository at this point in the history
Useful to get the lowest or highest element in a list in linear time, as
it avoid sorting the whole list.

Signed-off-by: Pau Ruiz Safont <[email protected]>
  • Loading branch information
psafont committed Jan 16, 2024
1 parent 7c5c22b commit 14f2990
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## Unreleased
- std: add Listext.List.find_first_sorted

## v4.23.0 (30-Oct-2023)
- unix: fix blkgetsize return type mismatch (CA-382014)
- unix: add function to recursively remove files
Expand Down
10 changes: 10 additions & 0 deletions lib/xapi-stdext-std/listext.ml
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,14 @@ module List = struct
aux ((upper - 1) :: accu) (upper - 1)
in
aux []

let find_first_sorted compare =
let rec loop ref = function
| [] ->
ref
| x :: xs ->
let ref = match compare ref x with c when c <= 0 -> ref | _ -> x in
loop ref xs
in
function [] -> None | ref :: xs -> Some (loop ref xs)
end
4 changes: 4 additions & 0 deletions lib/xapi-stdext-std/listext.mli
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ module List : sig
val is_sorted : ('a -> 'a -> int) -> 'a list -> bool
(** [is_sorted cmp l] returns whether [l] is sorted according to [cmp]. *)

val find_first_sorted : ('a -> 'a -> int) -> 'a list -> 'a option
(** [find_first_sorted cmp l] returns the first element in [l] according to
the sort order of [cmp], or [None] if the list is empty. *)

(** {1 Iterators} *)

val take : int -> 'a list -> 'a list
Expand Down
32 changes: 31 additions & 1 deletion lib/xapi-stdext-std/listext_test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,36 @@ let test_sub =
let tests = List.map test specs in
("sub", tests)

let test_find_first_sorted =
let ascending = (Int.compare, "ascending") in
let descending = ((fun a b -> Int.compare b a), "descending") in
let specs =
[
(ascending, [], None)
; (ascending, [1; 2; 3; 4; 5], Some 1)
; (ascending, [2; 3; 1; 5; 4], Some 1)
; (descending, [], None)
; (descending, [1; 2; 3; 4; 5], Some 5)
; (descending, [2; 3; 1; 5; 4], Some 5)
]
in
let test ((cmp, cmp_name), input, expected) =
let name =
Printf.sprintf "find_first_sorted %s of [%s]" cmp_name
(String.concat "; " (List.map string_of_int input))
in
test_option (Listext.find_first_sorted cmp) (name, input, expected)
in
let tests = List.map test specs in
("find_first_sorted", tests)

let () =
Alcotest.run "Listext"
[test_iteri_right; test_take; test_drop; test_chop; test_sub]
[
test_iteri_right
; test_take
; test_drop
; test_chop
; test_sub
; test_find_first_sorted
]

0 comments on commit 14f2990

Please sign in to comment.