ocaml/stdlib/list.ml

595 lines
15 KiB
OCaml

(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
(* An alias for the type of lists. *)
type 'a t = 'a list = [] | (::) of 'a * 'a list
(* List operations *)
let rec length_aux len = function
[] -> len
| _::l -> length_aux (len + 1) l
let length l = length_aux 0 l
let cons a l = a::l
let hd = function
[] -> failwith "hd"
| a::_ -> a
let tl = function
[] -> failwith "tl"
| _::l -> l
let nth l n =
if n < 0 then invalid_arg "List.nth" else
let rec nth_aux l n =
match l with
| [] -> failwith "nth"
| a::l -> if n = 0 then a else nth_aux l (n-1)
in nth_aux l n
let nth_opt l n =
if n < 0 then invalid_arg "List.nth" else
let rec nth_aux l n =
match l with
| [] -> None
| a::l -> if n = 0 then Some a else nth_aux l (n-1)
in nth_aux l n
let append = (@)
let rec rev_append l1 l2 =
match l1 with
[] -> l2
| a :: l -> rev_append l (a :: l2)
let rev l = rev_append l []
let rec init_tailrec_aux acc i n f =
if i >= n then acc
else init_tailrec_aux (f i :: acc) (i+1) n f
let rec init_aux i n f =
if i >= n then []
else
let r = f i in
r :: init_aux (i+1) n f
let rev_init_threshold =
match Sys.backend_type with
| Sys.Native | Sys.Bytecode -> 10_000
(* We don't know the size of the stack, better be safe and assume it's
small. *)
| Sys.Other _ -> 50
let init len f =
if len < 0 then invalid_arg "List.init" else
if len > rev_init_threshold then rev (init_tailrec_aux [] 0 len f)
else init_aux 0 len f
let rec flatten = function
[] -> []
| l::r -> l @ flatten r
let concat = flatten
let rec map f = function
[] -> []
| a::l -> let r = f a in r :: map f l
let rec mapi i f = function
[] -> []
| a::l -> let r = f i a in r :: mapi (i + 1) f l
let mapi f l = mapi 0 f l
let rev_map f l =
let rec rmap_f accu = function
| [] -> accu
| a::l -> rmap_f (f a :: accu) l
in
rmap_f [] l
let rec iter f = function
[] -> ()
| a::l -> f a; iter f l
let rec iteri i f = function
[] -> ()
| a::l -> f i a; iteri (i + 1) f l
let iteri f l = iteri 0 f l
let rec fold_left f accu l =
match l with
[] -> accu
| a::l -> fold_left f (f accu a) l
let rec fold_right f l accu =
match l with
[] -> accu
| a::l -> f a (fold_right f l accu)
let rec map2 f l1 l2 =
match (l1, l2) with
([], []) -> []
| (a1::l1, a2::l2) -> let r = f a1 a2 in r :: map2 f l1 l2
| (_, _) -> invalid_arg "List.map2"
let rev_map2 f l1 l2 =
let rec rmap2_f accu l1 l2 =
match (l1, l2) with
| ([], []) -> accu
| (a1::l1, a2::l2) -> rmap2_f (f a1 a2 :: accu) l1 l2
| (_, _) -> invalid_arg "List.rev_map2"
in
rmap2_f [] l1 l2
let rec iter2 f l1 l2 =
match (l1, l2) with
([], []) -> ()
| (a1::l1, a2::l2) -> f a1 a2; iter2 f l1 l2
| (_, _) -> invalid_arg "List.iter2"
let rec fold_left2 f accu l1 l2 =
match (l1, l2) with
([], []) -> accu
| (a1::l1, a2::l2) -> fold_left2 f (f accu a1 a2) l1 l2
| (_, _) -> invalid_arg "List.fold_left2"
let rec fold_right2 f l1 l2 accu =
match (l1, l2) with
([], []) -> accu
| (a1::l1, a2::l2) -> f a1 a2 (fold_right2 f l1 l2 accu)
| (_, _) -> invalid_arg "List.fold_right2"
let rec for_all p = function
[] -> true
| a::l -> p a && for_all p l
let rec exists p = function
[] -> false
| a::l -> p a || exists p l
let rec for_all2 p l1 l2 =
match (l1, l2) with
([], []) -> true
| (a1::l1, a2::l2) -> p a1 a2 && for_all2 p l1 l2
| (_, _) -> invalid_arg "List.for_all2"
let rec exists2 p l1 l2 =
match (l1, l2) with
([], []) -> false
| (a1::l1, a2::l2) -> p a1 a2 || exists2 p l1 l2
| (_, _) -> invalid_arg "List.exists2"
let rec mem x = function
[] -> false
| a::l -> compare a x = 0 || mem x l
let rec memq x = function
[] -> false
| a::l -> a == x || memq x l
let rec assoc x = function
[] -> raise Not_found
| (a,b)::l -> if compare a x = 0 then b else assoc x l
let rec assoc_opt x = function
[] -> None
| (a,b)::l -> if compare a x = 0 then Some b else assoc_opt x l
let rec assq x = function
[] -> raise Not_found
| (a,b)::l -> if a == x then b else assq x l
let rec assq_opt x = function
[] -> None
| (a,b)::l -> if a == x then Some b else assq_opt x l
let rec mem_assoc x = function
| [] -> false
| (a, _) :: l -> compare a x = 0 || mem_assoc x l
let rec mem_assq x = function
| [] -> false
| (a, _) :: l -> a == x || mem_assq x l
let rec remove_assoc x = function
| [] -> []
| (a, _ as pair) :: l ->
if compare a x = 0 then l else pair :: remove_assoc x l
let rec remove_assq x = function
| [] -> []
| (a, _ as pair) :: l -> if a == x then l else pair :: remove_assq x l
let rec find p = function
| [] -> raise Not_found
| x :: l -> if p x then x else find p l
let rec find_opt p = function
| [] -> None
| x :: l -> if p x then Some x else find_opt p l
let rec find_map f = function
| [] -> None
| x :: l ->
begin match f x with
| Some _ as result -> result
| None -> find_map f l
end
let find_all p =
let rec find accu = function
| [] -> rev accu
| x :: l -> if p x then find (x :: accu) l else find accu l in
find []
let filter = find_all
let filteri p l =
let rec aux i acc = function
| [] -> rev acc
| x::l -> aux (i + 1) (if p i x then x::acc else acc) l
in
aux 0 [] l
let filter_map f =
let rec aux accu = function
| [] -> rev accu
| x :: l ->
match f x with
| None -> aux accu l
| Some v -> aux (v :: accu) l
in
aux []
let concat_map f l =
let rec aux f acc = function
| [] -> rev acc
| x :: l ->
let xs = f x in
aux f (rev_append xs acc) l
in aux f [] l
let fold_left_map f accu l =
let rec aux accu l_accu = function
| [] -> accu, rev l_accu
| x :: l ->
let accu, x = f accu x in
aux accu (x :: l_accu) l in
aux accu [] l
let partition p l =
let rec part yes no = function
| [] -> (rev yes, rev no)
| x :: l -> if p x then part (x :: yes) no l else part yes (x :: no) l in
part [] [] l
let partition_map p l =
let rec part left right = function
| [] -> (rev left, rev right)
| x :: l ->
begin match p x with
| Either.Left v -> part (v :: left) right l
| Either.Right v -> part left (v :: right) l
end
in
part [] [] l
let rec split = function
[] -> ([], [])
| (x,y)::l ->
let (rx, ry) = split l in (x::rx, y::ry)
let rec combine l1 l2 =
match (l1, l2) with
([], []) -> []
| (a1::l1, a2::l2) -> (a1, a2) :: combine l1 l2
| (_, _) -> invalid_arg "List.combine"
(** sorting *)
let rec merge cmp l1 l2 =
match l1, l2 with
| [], l2 -> l2
| l1, [] -> l1
| h1 :: t1, h2 :: t2 ->
if cmp h1 h2 <= 0
then h1 :: merge cmp t1 l2
else h2 :: merge cmp l1 t2
let stable_sort cmp l =
let rec rev_merge l1 l2 accu =
match l1, l2 with
| [], l2 -> rev_append l2 accu
| l1, [] -> rev_append l1 accu
| h1::t1, h2::t2 ->
if cmp h1 h2 <= 0
then rev_merge t1 l2 (h1::accu)
else rev_merge l1 t2 (h2::accu)
in
let rec rev_merge_rev l1 l2 accu =
match l1, l2 with
| [], l2 -> rev_append l2 accu
| l1, [] -> rev_append l1 accu
| h1::t1, h2::t2 ->
if cmp h1 h2 > 0
then rev_merge_rev t1 l2 (h1::accu)
else rev_merge_rev l1 t2 (h2::accu)
in
let rec sort n l =
match n, l with
| 2, x1 :: x2 :: tl ->
let s = if cmp x1 x2 <= 0 then [x1; x2] else [x2; x1] in
(s, tl)
| 3, x1 :: x2 :: x3 :: tl ->
let s =
if cmp x1 x2 <= 0 then
if cmp x2 x3 <= 0 then [x1; x2; x3]
else if cmp x1 x3 <= 0 then [x1; x3; x2]
else [x3; x1; x2]
else if cmp x1 x3 <= 0 then [x2; x1; x3]
else if cmp x2 x3 <= 0 then [x2; x3; x1]
else [x3; x2; x1]
in
(s, tl)
| n, l ->
let n1 = n asr 1 in
let n2 = n - n1 in
let s1, l2 = rev_sort n1 l in
let s2, tl = rev_sort n2 l2 in
(rev_merge_rev s1 s2 [], tl)
and rev_sort n l =
match n, l with
| 2, x1 :: x2 :: tl ->
let s = if cmp x1 x2 > 0 then [x1; x2] else [x2; x1] in
(s, tl)
| 3, x1 :: x2 :: x3 :: tl ->
let s =
if cmp x1 x2 > 0 then
if cmp x2 x3 > 0 then [x1; x2; x3]
else if cmp x1 x3 > 0 then [x1; x3; x2]
else [x3; x1; x2]
else if cmp x1 x3 > 0 then [x2; x1; x3]
else if cmp x2 x3 > 0 then [x2; x3; x1]
else [x3; x2; x1]
in
(s, tl)
| n, l ->
let n1 = n asr 1 in
let n2 = n - n1 in
let s1, l2 = sort n1 l in
let s2, tl = sort n2 l2 in
(rev_merge s1 s2 [], tl)
in
let len = length l in
if len < 2 then l else fst (sort len l)
let sort = stable_sort
let fast_sort = stable_sort
(* Note: on a list of length between about 100000 (depending on the minor
heap size and the type of the list) and Sys.max_array_size, it is
actually faster to use the following, but it might also use more memory
because the argument list cannot be deallocated incrementally.
Also, there seems to be a bug in this code or in the
implementation of obj_truncate.
external obj_truncate : 'a array -> int -> unit = "caml_obj_truncate"
let array_to_list_in_place a =
let l = Array.length a in
let rec loop accu n p =
if p <= 0 then accu else begin
if p = n then begin
obj_truncate a p;
loop (a.(p-1) :: accu) (n-1000) (p-1)
end else begin
loop (a.(p-1) :: accu) n (p-1)
end
end
in
loop [] (l-1000) l
let stable_sort cmp l =
let a = Array.of_list l in
Array.stable_sort cmp a;
array_to_list_in_place a
*)
(** sorting + removing duplicates *)
let sort_uniq cmp l =
let rec rev_merge l1 l2 accu =
match l1, l2 with
| [], l2 -> rev_append l2 accu
| l1, [] -> rev_append l1 accu
| h1::t1, h2::t2 ->
let c = cmp h1 h2 in
if c = 0 then rev_merge t1 t2 (h1::accu)
else if c < 0
then rev_merge t1 l2 (h1::accu)
else rev_merge l1 t2 (h2::accu)
in
let rec rev_merge_rev l1 l2 accu =
match l1, l2 with
| [], l2 -> rev_append l2 accu
| l1, [] -> rev_append l1 accu
| h1::t1, h2::t2 ->
let c = cmp h1 h2 in
if c = 0 then rev_merge_rev t1 t2 (h1::accu)
else if c > 0
then rev_merge_rev t1 l2 (h1::accu)
else rev_merge_rev l1 t2 (h2::accu)
in
let rec sort n l =
match n, l with
| 2, x1 :: x2 :: tl ->
let s =
let c = cmp x1 x2 in
if c = 0 then [x1] else if c < 0 then [x1; x2] else [x2; x1]
in
(s, tl)
| 3, x1 :: x2 :: x3 :: tl ->
let s =
let c = cmp x1 x2 in
if c = 0 then
let c = cmp x2 x3 in
if c = 0 then [x2] else if c < 0 then [x2; x3] else [x3; x2]
else if c < 0 then
let c = cmp x2 x3 in
if c = 0 then [x1; x2]
else if c < 0 then [x1; x2; x3]
else
let c = cmp x1 x3 in
if c = 0 then [x1; x2]
else if c < 0 then [x1; x3; x2]
else [x3; x1; x2]
else
let c = cmp x1 x3 in
if c = 0 then [x2; x1]
else if c < 0 then [x2; x1; x3]
else
let c = cmp x2 x3 in
if c = 0 then [x2; x1]
else if c < 0 then [x2; x3; x1]
else [x3; x2; x1]
in
(s, tl)
| n, l ->
let n1 = n asr 1 in
let n2 = n - n1 in
let s1, l2 = rev_sort n1 l in
let s2, tl = rev_sort n2 l2 in
(rev_merge_rev s1 s2 [], tl)
and rev_sort n l =
match n, l with
| 2, x1 :: x2 :: tl ->
let s =
let c = cmp x1 x2 in
if c = 0 then [x1] else if c > 0 then [x1; x2] else [x2; x1]
in
(s, tl)
| 3, x1 :: x2 :: x3 :: tl ->
let s =
let c = cmp x1 x2 in
if c = 0 then
let c = cmp x2 x3 in
if c = 0 then [x2] else if c > 0 then [x2; x3] else [x3; x2]
else if c > 0 then
let c = cmp x2 x3 in
if c = 0 then [x1; x2]
else if c > 0 then [x1; x2; x3]
else
let c = cmp x1 x3 in
if c = 0 then [x1; x2]
else if c > 0 then [x1; x3; x2]
else [x3; x1; x2]
else
let c = cmp x1 x3 in
if c = 0 then [x2; x1]
else if c > 0 then [x2; x1; x3]
else
let c = cmp x2 x3 in
if c = 0 then [x2; x1]
else if c > 0 then [x2; x3; x1]
else [x3; x2; x1]
in
(s, tl)
| n, l ->
let n1 = n asr 1 in
let n2 = n - n1 in
let s1, l2 = sort n1 l in
let s2, tl = sort n2 l2 in
(rev_merge s1 s2 [], tl)
in
let len = length l in
if len < 2 then l else fst (sort len l)
let rec compare_lengths l1 l2 =
match l1, l2 with
| [], [] -> 0
| [], _ -> -1
| _, [] -> 1
| _ :: l1, _ :: l2 -> compare_lengths l1 l2
;;
let rec compare_length_with l n =
match l with
| [] ->
if n = 0 then 0 else
if n > 0 then -1 else 1
| _ :: l ->
if n <= 0 then 1 else
compare_length_with l (n-1)
;;
(** {1 Comparison} *)
(* Note: we are *not* shortcutting the list by using
[List.compare_lengths] first; this may be slower on long lists
immediately start with distinct elements. It is also incorrect for
[compare] below, and it is better (principle of least surprise) to
use the same approach for both functions. *)
let rec equal eq l1 l2 =
match l1, l2 with
| [], [] -> true
| [], _::_ | _::_, [] -> false
| a1::l1, a2::l2 -> eq a1 a2 && equal eq l1 l2
let rec compare cmp l1 l2 =
match l1, l2 with
| [], [] -> 0
| [], _::_ -> -1
| _::_, [] -> 1
| a1::l1, a2::l2 ->
let c = cmp a1 a2 in
if c <> 0 then c
else compare cmp l1 l2
(** {1 Iterators} *)
let to_seq l =
let rec aux l () = match l with
| [] -> Seq.Nil
| x :: tail -> Seq.Cons (x, aux tail)
in
aux l
let of_seq seq =
let rec direct depth seq : _ list =
if depth=0
then
Seq.fold_left (fun acc x -> x::acc) [] seq
|> rev (* tailrec *)
else match seq() with
| Seq.Nil -> []
| Seq.Cons (x, next) -> x :: direct (depth-1) next
in
direct 500 seq