(* * BatList - additional and modified functions for lists. * Copyright (C) 2003 Brian Hurt * Copyright (C) 2003 Nicolas Cannasse * Copyright (C) 2008 Red Hat Inc. * Copyright (C) 2008 David Rajchenbach-Teller, LIFO, Universite d'Orleans * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version, * with the special exception on linking described in file LICENSE. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) ##V>=5##module Pervasives = Stdlib (*$inject ##V>=5##module Pervasives = Stdlib *) (* ::VH:: GLUE with StdLib *) let merge = List.merge let fast_sort = List.fast_sort let stable_sort = List.stable_sort let sort = List.sort let assq = List.assq ##V>=4.5##let assq_opt = List.assq_opt ##V<4.5##let assq_opt k li = try Some (assq k li) with Not_found -> None let assoc = List.assoc ##V>=4.5##let assoc_opt = List.assoc_opt ##V<4.5##let assoc_opt k li = try Some (assoc k li) with Not_found -> None let find = List.find ##V>=4.5##let find_opt = List.find_opt ##V<4.5##let find_opt p li = try Some (find p li) with Not_found -> None let exists = List.exists let for_all = List.for_all let fold_left = List.fold_left let fold = List.fold_left let rev_map = List.rev_map let iter = List.iter let rev_append = List.rev_append let rev = List.rev let length = List.length ##V>=4.5##let compare_length_with = List.compare_length_with ##V>=4.5##let compare_lengths = List.compare_lengths let tl = List.tl let hd = List.hd let mem = List.mem let memq = List.memq let mem_assq = List.mem_assq let mem_assoc = List.mem_assoc let rev_map2 = List.rev_map2 ##V>=4.07##let to_seq = List.to_seq ##V>=4.07##let of_seq = List.of_seq ##V>=4.10##let concat_map = List.concat_map ##V>=4.10##let find_map_opt = List.find_map ##V>=4.12##let equal = List.equal (* ::VH:: END GLUE *) let rec compare_lengths la lb = match la, lb with | [], [] -> 0 | [], _::_ -> -1 | _::_, [] -> 1 | _::la, _::lb -> compare_lengths la lb (*$T compare_lengths compare_lengths [] [] = 0 compare_lengths [] [1] = -1 compare_lengths [1] [] = 1 compare_lengths [1; 2] [3; 4] = 0 compare_lengths [1; 2; 3] [3; 4] = 1 compare_lengths [1; 2] [2; 3; 4] = -1 *) (*$Q compare_lengths (Q.pair (Q.list Q.small_int) (Q.list Q.small_int)) \ (fun (la, lb) -> \ BatOrd.ord0 (compare_lengths la lb) \ = BatOrd.ord0 (BatInt.compare (length la) (length lb))) *) let rec compare_length_with li n = match li, n with | [], n -> Pervasives.compare 0 n | _::tl, n -> compare_length_with tl (n-1) (*$T compare_length_with compare_length_with [] 0 = 0 compare_length_with [] 1 = -1 compare_length_with [1] 0 = 1 compare_length_with [1; 2] 2 = 0 compare_length_with [1; 2; 3] 2 = 1 compare_length_with [1; 2] 3 = -1 *) (*$Q compare_length_with (Q.pair (Q.list Q.small_int) Q.small_int) \ (fun (li, n) -> \ BatOrd.ord0 (compare_length_with li n) \ = BatOrd.ord0 (BatInt.compare (length li) n)) *) (* Thanks to Jacques Garrigue for suggesting the following structure *) type 'a mut_list = { hd: 'a; mutable tl: 'a list } ##V<4.08##type 'a t = 'a list ##V>=4.08##type 'a t = 'a list = [] | (::) of 'a * 'a list type 'a enumerable = 'a t type 'a mappable = 'a t external inj : 'a mut_list -> 'a list = "%identity" module Acc = struct let dummy () = { hd = Obj.magic (); tl = [] } let create x = { hd = x; tl = [] } let accum acc x = let cell = create x in acc.tl <- inj cell; cell end let cons h t = h::t let is_empty = function | [] -> true | _ -> false (*$T is_empty is_empty [] not (is_empty [1]) *) let at_negative_index_msg = "List: Negative index not allowed" let at_after_end_msg = "List: Index past end of list" let nth l index = if index < 0 then invalid_arg at_negative_index_msg; let rec loop n = function | [] -> invalid_arg at_after_end_msg; | h :: t -> if n = 0 then h else loop (n - 1) t in loop index l let at = nth (*$T at try ignore (at [] 0); false with Invalid_argument _ -> true try ignore (at [1;2;3] (-1)); false with Invalid_argument _ -> true at [1;2;3] 2 = 3 *) let at_opt l index = if index < 0 then invalid_arg at_negative_index_msg; try Some (at l index) with Invalid_argument _ -> None (*$T at_opt at_opt [] 0 = None try ignore (at_opt [1;2;3] (-1)); false with Invalid_argument _ -> true at_opt [1;2;3] 2 = Some 3 *) let mem_cmp cmp x l = exists (fun y -> cmp x y = 0) l (*$T mem_cmp mem_cmp BatInt.compare 0 [] = false mem_cmp BatInt.compare 0 [1; 2] = false mem_cmp BatInt.compare 1 [1; 2] = true mem_cmp BatInt.compare 2 [1; 2] = true *) let append l1 l2 = match l1 with | [] -> l2 | h :: t -> let rec loop dst = function | [] -> dst.tl <- l2 | h :: t -> loop (Acc.accum dst h) t in let r = Acc.create h in loop r t; inj r (*$T append append [] [] = [] append [] [1] = [1] append [1] [] = [1] append [1] [2] = [1; 2] append [1; 2] [3] = [1; 2; 3] append [1] [2; 3] = [1; 2; 3] *) let flatten l = let rec inner dst = function | [] -> dst | h :: t -> inner (Acc.accum dst h) t in let rec outer dst = function | [] -> () | h :: t -> outer (inner dst h) t in let r = Acc.dummy () in outer r l; r.tl let concat = flatten (*$T flatten flatten [[1;2];[3];[];[4;5;6]] = [1;2;3;4;5;6] flatten [[]] = [] *) let singleton x = [x] (*$Q singleton Q.int (fun x -> let s = singleton x in hd s = x && length s = 1) *) let map f = function | [] -> [] | h :: t -> let rec loop dst = function | [] -> () | h :: t -> loop (Acc.accum dst (f h)) t in let r = Acc.create (f h) in loop r t; inj r (*$Q map (Q.pair (Q.fun1 Q.Observable.int Q.int) (Q.list Q.small_int)) \ (fun (Q.Fun (_,f),l) -> map f l = List.map f l) *) let rec drop n = function | _ :: l when n > 0 -> drop (n-1) l | l -> l (*$= drop & ~printer:(IO.to_string (List.print Int.print)) (drop 0 [1;2;3]) [1;2;3] (drop 3 [1;2;3]) [] (drop 4 [1;2;3]) [] (drop 1 [1;2;3]) [2;3] *) let take n l = let rec loop n dst = function | h :: t when n > 0 -> loop (n - 1) (Acc.accum dst h) t | _ -> () in let dummy = Acc.dummy () in loop n dummy l; dummy.tl (*$= take & ~printer:(IO.to_string (List.print Int.print)) (take 0 [1;2;3]) [] (take 3 [1;2;3]) [1;2;3] (take 4 [1;2;3]) [1;2;3] (take 1 [1;2;3]) [1] *) let takedrop n l = let rec loop n dst = function | h :: t when n > 0 -> loop (n - 1) (Acc.accum dst h) t | rest -> rest in let dummy = Acc.dummy () in let rest = loop n dummy l in (dummy.tl, rest) (*$T takedrop takedrop 0 [1; 2; 3] = ([], [1; 2; 3]) takedrop 3 [1; 2; 3] = ([1; 2; 3], []) takedrop 4 [1; 2; 3] = ([1; 2; 3], []) takedrop 1 [1; 2; 3] = ([1], [2; 3]) *) let ntake n l = if n < 1 then invalid_arg "List.ntake"; let took, left = takedrop n l in let acc = Acc.create took in let rec loop dst = function | [] -> inj acc | li -> let taken, rest = takedrop n li in loop (Acc.accum dst taken) rest in loop acc left (*$T ntake ntake 2 [] = [[]] ntake 2 [1] = [[1]] ntake 2 [1; 2] = [[1; 2]] ntake 2 [1; 2; 3] = [[1; 2]; [3]] ntake 2 [1; 2; 3; 4] = [[1; 2]; [3; 4]] *) let take_while p li = let rec loop dst = function | [] -> () | x :: xs -> if p x then loop (Acc.accum dst x) xs in let dummy = Acc.dummy () in loop dummy li; dummy.tl (*$= take_while & ~printer:(IO.to_string (List.print Int.print)) (take_while ((=) 3) [3;3;4;3;3]) [3;3] (take_while ((=) 3) [3]) [3] (take_while ((=) 3) [4]) [] (take_while ((=) 3) []) [] (take_while ((=) 2) [2; 2]) [2; 2] *) let rec drop_while f = function | [] -> [] | x :: xs when f x -> drop_while f xs | xs -> xs (*$= drop_while & ~printer:(IO.to_string (List.print Int.print)) (drop_while ((=) 3) [3;3;4;3;3]) [4;3;3] (drop_while ((=) 3) [3]) [] *) let span p li = let rec loop dst = function | [] -> [] | x :: xs as l -> if p x then loop (Acc.accum dst x) xs else l in let dummy = Acc.dummy () in let xs = loop dummy li in (dummy.tl , xs) (*$= span (span ((=) 3) [3;3;4;3;3]) ([3;3],[4;3;3]) (span ((=) 3) [3]) ([3],[]) (span ((=) 3) [4]) ([],[4]) (span ((=) 3) []) ([],[]) (span ((=) 2) [2; 2]) ([2; 2],[]) *) let fold_while p f init li = let rec loop acc = function | [] -> (acc, []) | (x :: xs) as l -> if p acc x then loop (f acc x) xs else (acc, l) in loop init li (*$= fold_while (fold_while (fun _acc x -> x = 3) (fun acc x -> acc + x) 0 [3;3;4;3;3]) (6,[4;3;3]) (fold_while (fun acc _x -> acc < 6) (fun acc x -> acc + x) 0 [3;3;4;3;3]) (6,[4;3;3]) (fold_while (fun _acc x -> x = 3) (fun acc x -> acc + x) 0 [3]) (3,[]) (fold_while (fun _acc x -> x = 3) (fun acc x -> acc + x) 0 [4]) (0,[4]) (fold_while (fun _acc x -> x = 3) (fun acc x -> acc + x) 0 []) (0,[]) (fold_while (fun _acc x -> x = 2) (fun acc x -> acc + x) 0 [2; 2]) (4,[]) *) let nsplit p = function | [] -> [] (* note that returning [] on empty inputs is an arbitrary choice that is made for consistence with the behavior of BatString.nsplit. Not having this hardcoded case would have `nsplit p []` return `[[]]`, which is also a semantically valid return value (in fact the two are equivalent, but `[[]]` would be a more natural choice as it allows to enforce the simply invariant that `nsplit` return values are always non-empty). If that was to redo from scratch, `[[]]` would be a better return value for both `BatList.nsplit` and `BatString.nsplit`. *) | li -> let not_p x = not (p x) in let rec loop dst l = let ok, rest = span not_p l in let r = Acc.accum dst ok in match rest with | [] -> () | _x :: xs -> loop r xs in let dummy = Acc.dummy () in loop dummy li; dummy.tl (*$T nsplit nsplit ((=) 0) [] = [] nsplit ((=) 0) [0] = [[]; []] nsplit ((=) 0) [1; 0] = [[1]; []] nsplit ((=) 0) [0; 1] = [[]; [1]] nsplit ((=) 0) [1; 2; 0; 0; 3; 4; 0; 5] = [[1; 2]; []; [3; 4]; [5]] *) (*$Q nsplit & ~count:10 (Q.list (Q.list Q.pos_int)) (fun xss -> \ let join sep xss = flatten (interleave [sep] xss) in \ (* normalize: the return type of nsplit \ is quotiented by the equivalence []~[[]] *) \ let normalize = function [] -> [[]] | li -> li in \ let neg = -1 in \ normalize xss = normalize (nsplit ((=) neg) (join neg xss)) \ ) (Q.pair Q.small_int (Q.list Q.small_int)) (fun (sep,xs) -> \ let join sep xss = flatten (interleave [sep] xss) in \ xs = join sep (nsplit ((=) sep) xs) \ ) *) (* nsplit ((=) sep) la @ nsplit ((=) sep) lb = nsplit ((=) sep) (la @ [sep] @ lb) *) let group_consecutive p l = let rec loop dst = function | [] -> () | x :: rest -> let xs, rest = span (p x) rest in loop (Acc.accum dst (x :: xs)) rest in let dummy = Acc.dummy () in loop dummy l; dummy.tl (*$= group_consecutive & ~printer:(IO.to_string (List.print (List.print Int.print))) (group_consecutive (=) [3; 3; 4; 3; 3]) [[3; 3]; [4]; [3; 3]] (group_consecutive (=) [3]) [[3]] (group_consecutive (=) []) [] (group_consecutive (=) [2; 2]) [[2; 2]] *) ##V>=4.5##let nth_opt = List.nth_opt ##V<4.5##let nth_opt li n = try Some (nth li n) with _ -> None let takewhile = take_while let dropwhile = drop_while let interleave ?first ?last (sep:'a) (l:'a list) = let may_prepend maybe_x lst = match maybe_x with | None -> lst | Some x -> x :: lst in let rec loop acc = function | [] -> acc | x :: xs -> match acc with | [] -> loop [x] xs | _ -> loop (x :: sep :: acc) xs in let res = loop [] l in may_prepend first (rev (may_prepend last res)) (*$= interleave & ~printer:(IO.to_string (List.print Int.print)) (interleave 0 [1;2;3]) [1;0;2;0;3] (interleave 0 [1]) [1] (interleave 0 []) [] (interleave ~first:(-1) 0 [1;2;3]) [-1;1;0;2;0;3] (interleave ~first:(-1) 0 [1]) [-1;1] (interleave ~first:(-1) 0 []) [-1] (interleave ~last:(-2) 0 [1;2;3]) [1;0;2;0;3;-2] (interleave ~last:(-2) 0 [1]) [1;-2] (interleave ~last:(-2) 0 []) [-2] (interleave ~first:(-1) ~last:(-2) 0 [1;2;3]) [-1;1;0;2;0;3;-2] (interleave ~first:(-1) ~last:(-2) 0 [1]) [-1;1;-2] (interleave ~first:(-1) ~last:(-2) 0 []) [-1;-2] *) let unique ?(eq = ( = )) l = let rec loop dst = function | [] -> () | h :: t -> match exists (eq h) t with | true -> loop dst t | false -> loop (Acc.accum dst h) t in let dummy = Acc.dummy () in loop dummy l; dummy.tl (* FIXME BAD TESTS: RESULT IS SPECIFIC TO IMPLEMENTATION *) (*$= unique & ~printer:(IO.to_string (List.print Int.print)) [1;2;3;4;5;6] (unique [1;1;2;2;3;3;4;5;6;4;5;6]) [1] (unique [1;1;1;1;1;1;1;1;1;1]) [1;2] (unique ~eq:(fun x y -> x land 1 = y land 1) [2;2;2;4;6;8;3;1;2]) *) let unique_cmp ?(cmp = Pervasives.compare) l = let set = ref (BatSet.PSet.create cmp) in let should_keep x = if BatSet.PSet.mem x !set then false else ( set := BatSet.PSet.add x !set; true ) in (* use a stateful filter to remove duplicate elements *) List.filter should_keep l (*$= unique_cmp & ~printer:(IO.to_string (List.print Int.print)) [1;2;3;4;5;6] (unique_cmp [1;1;2;2;3;3;4;5;6;4;5;6]) [1] (unique_cmp [1;1;1;1;1;1;1;1;1;1]) [2;3] (unique_cmp ~cmp:(fun x y -> Int.compare (x land 1) (y land 1)) [2;2;2;4;6;8;3;1;2]) *) let unique_hash (type et) ?(hash = Hashtbl.hash) ?(eq = (=)) (l : et list) = let module HT = Hashtbl.Make(struct type t = et let equal = eq let hash = hash end) in let ht = HT.create (List.length l) in let rec loop dst = function | h::t when not (HT.mem ht h) -> HT.add ht h (); (* put h in hash table *) loop (Acc.accum dst h) (* and to output list *) t | _::t -> (* if already in hashtable then don't add to output list *) loop dst t | [] -> () in let dummy = Acc.dummy () in loop dummy l; dummy.tl (*$= unique_hash & ~printer:(IO.to_string (List.print Int.print)) [1;2;3;4;5;6] (unique_hash [1;1;2;2;3;3;4;5;6;4;5;6]) [1] (unique_hash [1;1;1;1;1;1;1;1;1;1]) [2;3] (unique_hash ~hash:(fun x -> Hashtbl.hash (x land 1)) ~eq:(fun x y -> x land 1 = y land 1) [2;2;2;4;6;8;3;1;2]) *) let filter_map f l = let rec loop dst = function | [] -> () | h :: t -> match f h with | None -> loop dst t | Some x -> loop (Acc.accum dst x) t in let dummy = Acc.dummy () in loop dummy l; dummy.tl let filteri_map f l = let rec loop i dst = function | [] -> () | h :: t -> match f i h with | None -> loop (succ i) dst t | Some x -> loop (succ i) (Acc.accum dst x) t in let dummy = Acc.dummy () in loop 0 dummy l; dummy.tl (*$T filteri_map (let r = ref (-1) in filteri_map (fun i _ -> incr r; if i = !r then Some i else None) [5; 4; 8] = [0; 1; 2]) filteri_map (fun _ x -> if x > 4 then Some (x, string_of_int x) else None) [5; 4; 8] = [(5, "5"); (8, "8")] filteri_map (fun _ _ -> Some ()) [] = [] filteri_map (fun _ _ -> None) [1; 2] = [] *) let rec find_map f = function | [] -> raise Not_found | x :: xs -> match f x with | Some y -> y | None -> find_map f xs let fold_right_max = 1000 let fold_right f l init = let rec tail_loop acc = function | [] -> acc | h :: t -> tail_loop (f h acc) t in let rec loop n = function | [] -> init | h :: t -> if n < fold_right_max then f h (loop (n+1) t) else f h (tail_loop init (rev t)) in loop 0 l let map2 f l1 l2 = let rec loop dst src1 src2 = match src1, src2 with | [], [] -> () | h1 :: t1, h2 :: t2 -> loop (Acc.accum dst (f h1 h2)) t1 t2 | _ -> invalid_arg "List.map2: list lengths differ" in let dummy = Acc.dummy () in loop dummy l1 l2; dummy.tl let map2i f l1 l2 = let rec loop i dst src1 src2 = match src1, src2 with | [], [] -> () | h1 :: t1, h2 :: t2 -> loop (succ i) (Acc.accum dst (f i h1 h2)) t1 t2 | _ -> invalid_arg "List.map2i: list lengths differ" in let dummy = Acc.dummy () in loop 0 dummy l1 l2; dummy.tl (*$T map2i map2i (fun i x y -> i, x, y) [] [] = [] map2i (fun i x y -> i, x, y) ['a'] ["b"] = [0, 'a', "b"] map2i (fun i x y -> i, x, y) ['a'; 'b'; 'c'] ["d"; "e"; "f"] = \ [(0, 'a', "d"); (1, 'b', "e"); (2, 'c', "f")] try ignore (map2i (fun i x y -> i, x, y) [] [0]); false \ with Invalid_argument _ -> true try ignore (map2i (fun i x y -> i, x, y) [1; 2; 3] ["4"]); false \ with Invalid_argument _ -> true *) let rec iter2 f l1 l2 = match l1, l2 with | [], [] -> () | h1 :: t1, h2 :: t2 -> f h1 h2; iter2 f t1 t2 | _ -> invalid_arg "List.iter2: list lengths differ" let iter2i f l1 l2 = let rec loop i l1 l2 = match l1, l2 with | [], [] -> () | h1 :: t1, h2 :: t2 -> f i h1 h2; loop (succ i) t1 t2 | _ -> invalid_arg "List.iter2i: list lengths differ" in loop 0 l1 l2 (*$T iter2i try iter2i (fun _ _ _ -> ()) [1] [1;2;3]; false \ with Invalid_argument _ -> true try iter2i (fun _ _ _ -> ()) [1] []; false \ with Invalid_argument _ -> true *) (*$T iter2i iter2i (fun _ _ _ -> assert false) [] []; true let r = ref 0 in iter2i (fun i x y -> r := !r + i * x + y) [1] [2]; !r = 2 let r = ref 0 in iter2i (fun i x y -> r := !r + i * x + y) [1; 2] [3; 4]; !r = 9 *) let rec fold_left2 f accum l1 l2 = match l1, l2 with | [], [] -> accum | h1 :: t1, h2 :: t2 -> fold_left2 f (f accum h1 h2) t1 t2 | _ -> invalid_arg "List.fold_left2: list lengths differ" let fold_right2 f l1 l2 init = let rec tail_loop acc l1 l2 = match l1, l2 with | [] , [] -> acc | h1 :: t1 , h2 :: t2 -> tail_loop (f h1 h2 acc) t1 t2 | _ -> invalid_arg "List.fold_right2: list lengths differ" in let rec loop n l1 l2 = match l1, l2 with | [], [] -> init | h1 :: t1, h2 :: t2 -> if n < fold_right_max then f h1 h2 (loop (n+1) t1 t2) else f h1 h2 (tail_loop init (rev t1) (rev t2)) | _ -> invalid_arg "List.fold_right2: list lengths differ" in loop 0 l1 l2 let for_all2 p l1 l2 = let rec loop l1 l2 = match l1, l2 with | [], [] -> true | h1 :: t1, h2 :: t2 -> if p h1 h2 then loop t1 t2 else false | _ -> invalid_arg "List.for_all2: list lengths differ" in loop l1 l2 let exists2 p l1 l2 = let rec loop l1 l2 = match l1, l2 with | [], [] -> false | h1 :: t1, h2 :: t2 -> if p h1 h2 then true else loop t1 t2 | _ -> invalid_arg "List.exists2: list lengths differ" in loop l1 l2 let remove_assoc x lst = let rec loop dst = function | [] -> () | (a, _ as pair) :: t -> if a = x then dst.tl <- t else loop (Acc.accum dst pair) t in let dummy = Acc.dummy () in loop dummy lst; dummy.tl let remove_assq x lst = let rec loop dst = function | [] -> () | (a, _ as pair) :: t -> if a == x then dst.tl <- t else loop (Acc.accum dst pair) t in let dummy = Acc.dummy () in loop dummy lst; dummy.tl let remove_at i lst = let rec loop dst i = function | [] -> invalid_arg "List.remove_at" | x :: xs -> if i = 0 then dst.tl <- xs else loop (Acc.accum dst x) (i - 1) xs in if i < 0 then invalid_arg "List.remove_at" else let dummy = Acc.dummy () in loop dummy i lst; dummy.tl (*$T remove_at try ignore (remove_at 0 []) ; false with Invalid_argument _ -> true try ignore (remove_at 1 [0]); false with Invalid_argument _ -> true remove_at 0 [0] = [] remove_at 0 [0; 1; 2] = [1; 2] remove_at 1 [0; 1; 2] = [0; 2] remove_at 2 [0; 1; 2] = [0; 1] *) let rfind p l = find p (rev l) let find_all p l = let rec findnext dst = function | [] -> () | h :: t -> if p h then findnext (Acc.accum dst h) t else findnext dst t in let dummy = Acc.dummy () in findnext dummy l; dummy.tl let findi p l = let rec loop n = function | [] -> raise Not_found | h :: t -> if p n h then (n,h) else loop (n+1) t in loop 0 l let index_of e l = let rec loop n = function | [] -> None | h::_ when h = e -> Some n | _::t -> loop ( n + 1 ) t in loop 0 l let index_ofq e l = let rec loop n = function | [] -> None | h::_ when h == e -> Some n | _::t -> loop ( n + 1 ) t in loop 0 l let rindex_of e l = let rec loop n acc = function | [] -> acc | h::t when h = e -> loop ( n + 1) ( Some n ) t | _::t -> loop ( n + 1 ) acc t in loop 0 None l let rindex_ofq e l = let rec loop n acc = function | [] -> acc | h::t when h == e -> loop ( n + 1) ( Some n ) t | _::t -> loop ( n + 1 ) acc t in loop 0 None l let filter = find_all let count_matching p l = fold_left (fun count x -> if p x then count + 1 else count ) 0 l (*$T count_matching count_matching (fun _ -> true) [] = 0 count_matching (fun _ -> true) [1] = 1 count_matching (fun _ -> true) [1;2] = 2 count_matching (fun x -> x mod 2 = 1) [1;2;3;4;5;6] = 3 *) ##V>=4.11##let filteri = List.filteri ##V<4.11##let filteri f = ##V<4.11## let rec aux i = function ##V<4.11## | [] -> [] ##V<4.11## | x::xs when f i x -> x :: aux (succ i) xs ##V<4.11## | _x::xs -> aux (succ i) xs ##V<4.11## in ##V<4.11## aux 0 (*$T filteri (let r = ref (-1) in filteri (fun i _ -> incr r; i = !r) [5; 4; 8] = [5; 4; 8]) filteri (fun _ x -> x > 4) [5; 4; 8] = [5; 8] filteri (fun _ _ -> true) [] = [] *) let partition p lst = let rec loop yesdst nodst = function | [] -> () | h :: t -> if p h then loop (Acc.accum yesdst h) nodst t else loop yesdst (Acc.accum nodst h) t in let yesdummy = Acc.dummy () and nodummy = Acc.dummy () in loop yesdummy nodummy lst; (yesdummy.tl, nodummy.tl) let partition_map p lst = let rec loop left right = function | [] -> () | x :: xs -> match p x with | BatEither.Left v -> loop (Acc.accum left v) right xs | BatEither.Right v -> loop left (Acc.accum right v) xs in let left_acc = Acc.dummy () and right_acc = Acc.dummy () in loop left_acc right_acc lst; (left_acc.tl, right_acc.tl) (*$T partition_map let odd_or_even x = \ if x mod 2 = 1 then BatEither.Left x else BatEither.Right x in \ partition_map odd_or_even [1;2;3;4;5;6] = ([1;3;5], [2;4;6]) *) let split lst = let rec loop adst bdst = function | [] -> () | (a, b) :: t -> loop (Acc.accum adst a) (Acc.accum bdst b) t in let adummy = Acc.dummy () and bdummy = Acc.dummy () in loop adummy bdummy lst; adummy.tl, bdummy.tl let combine l1 l2 = match l1, l2 with | [], [] -> [] | x :: xs, y :: ys -> let acc = Acc.create (x, y) in let rec loop dst l1 l2 = match l1, l2 with | [], [] -> inj acc | h1 :: t1, h2 :: t2 -> loop (Acc.accum dst (h1, h2)) t1 t2 | _, _ -> invalid_arg "List.combine: list lengths differ" in loop acc xs ys | _, _ -> invalid_arg "List.combine: list lengths differ" (*$T combine combine [] [] = [] combine [1] [2] = [(1, 2)] combine [1; 3] [2; 4] = [(1, 2); (3, 4)] *) let init size f = if size = 0 then [] else if size < 0 then invalid_arg "BatList.init" else let rec loop dst n = if n < size then loop (Acc.accum dst (f n)) (n+1) in let r = Acc.create (f 0) in loop r 1; inj r let unfold_exn f = let rec loop dst = loop (Acc.accum dst (f ())) in let acc = Acc.dummy () in try loop acc with exn -> (acc.tl, exn) (*$T unfold_exn let exc () = raise End_of_file in \ unfold_exn exc = ([], End_of_file) let state = ref 0 in \ let just_zero () = \ if !state = 1 then raise End_of_file \ else let _ = incr state in 0 \ in \ unfold_exn just_zero = ([0], End_of_file) *) let unfold_exc = unfold_exn let make i x = if i < 0 then invalid_arg "List.make"; let rec loop x acc = function | 0 -> acc | i -> loop x (x::acc) (i-1) in loop x [] i let range i dir j = let op = match dir with | `To -> if i > j then invalid_arg (Printf.sprintf "List.range %d `To %d" i j) else pred | `Downto -> if i < j then invalid_arg (Printf.sprintf "List.range %d `Downto %d" i j) else succ in let rec loop acc k = if i = k then k :: acc else loop (k :: acc) (op k) in loop [] j (*$T range range 1 `To 3 = [1; 2; 3] range 1 `To 1 = [1] range 3 `Downto 1 = [3; 2; 1] range 3 `Downto 3 = [3] try ignore(range 1 `To 0); true with Invalid_argument _ -> true try ignore(range 1 `Downto 2); true with Invalid_argument _ -> true *) let frange start direction stop n = if n < 2 then invalid_arg (Printf.sprintf "List.frange: %d < 2" n); let nb_steps = float_of_int (n - 1) in match direction with | `To -> begin if start >= stop then invalid_arg (Printf.sprintf "List.frange %f `To %f" start stop); let span = stop -. start in let rec loop acc i = let x = ((span *. float_of_int (i - 1)) /. nb_steps) +. start in let acc' = x :: acc in if i = 1 then acc' else loop acc' (i - 1) in loop [] n end | `Downto -> begin if start <= stop then invalid_arg (Printf.sprintf "List.frange %f `Downto %f" start stop); let span = start -. stop in let rec loop acc i = let x = ((span *. float_of_int (i - 1)) /. nb_steps) +. stop in let acc' = x :: acc in if i = n then acc' else loop acc' (i + 1) in loop [] 1 end (*$T frange try ignore(frange 1. `To 2. 1); true with Invalid_argument _ -> true try ignore(frange 2. `Downto 1. 1); true with Invalid_argument _ -> true try ignore(frange 3. `To 1. 3); true with Invalid_argument _ -> true try ignore(frange 1. `Downto 3. 3); true with Invalid_argument _ -> true frange 1. `To 3. 3 = [1.; 2.; 3.] frange 1. `To 2. 2 = [1.; 2.] frange 3. `Downto 1. 3 = [3.; 2.; 1.] frange 2. `Downto 1. 2 = [2.; 1.] length (frange 0.123 `To 3.491 1000) = 1000 *) let mapi f = function | [] -> [] | h :: t -> let rec loop dst n = function | [] -> () | h :: t -> loop (Acc.accum dst (f n h)) (n + 1) t in let r = Acc.create (f 0 h) in loop r 1 t; inj r let iteri f l = let rec loop n = function | [] -> () | h :: t -> f n h; loop (n+1) t in loop 0 l let fold_lefti f init l = let rec loop i acc = function | [] -> acc | x :: xs -> loop (i + 1) (f acc i x) xs in loop 0 init l (*$T fold_lefti fold_lefti (fun acc i x -> (i, x) :: acc) [] [] = [] fold_lefti (fun acc i x -> (i, x) :: acc) [] [0.] = [(0, 0.)] fold_lefti (fun acc i x -> (i, x) :: acc) [] [0.; 1.] = [(1, 1.); (0, 0.)] *) let fold_righti f l init = let xis = (* reverse the list and index its elements *) fold_lefti (fun acc i x -> (i, x) :: acc) [] l in fold_left (fun acc (i, x) -> f i x acc) init xis (*$T fold_righti fold_righti (fun i x acc -> (i, x) :: acc) [] [] = [] fold_righti (fun i x acc -> (i, x) :: acc) [0.] [] = [(0, 0.)] fold_righti (fun i x acc -> (i, x) :: acc) [0.; 1.] [] = [(0, 0.); (1, 1.)] *) ##V>=4.11##let fold_left_map = List.fold_left_map ##V<4.11##let fold_left_map f acc = function ##V<4.11## | [] -> acc, [] ##V<4.11## | h :: t -> ##V<4.11## let rec loop acc dst = function ##V<4.11## | [] -> acc ##V<4.11## | h :: t -> ##V<4.11## let acc', t' = f acc h in ##V<4.11## loop acc' (Acc.accum dst t') t ##V<4.11## in ##V<4.11## let acc', h' = f acc h in ##V<4.11## let r = Acc.create h' in ##V<4.11## let res = loop acc' r t in ##V<4.11## res, inj r (*$T fold_left_map fold_left_map (fun acc x -> assert false) 0 [] = (0, []) fold_left_map (fun acc x -> acc ^ x, int_of_string x) "0" ["1"; "2"; "3"] = ("0123", [1; 2; 3]) *) let first = hd let rec last = function | [] -> invalid_arg "Empty List" | h :: [] -> h | _ :: t -> last t let split_nth index = function | [] -> if index = 0 then [],[] else invalid_arg at_after_end_msg | (h :: t as l) -> if index = 0 then [],l else if index < 0 then invalid_arg at_negative_index_msg else let rec loop n dst l = if n = 0 then l else match l with | [] -> invalid_arg at_after_end_msg | h :: t -> loop (n - 1) (Acc.accum dst h) t in let r = Acc.create h in inj r, loop (index-1) r t let split_at = split_nth let find_exn f e l = try find f l with Not_found -> raise e let remove l x = let rec loop dst = function | [] -> () | h :: t -> if x = h then dst.tl <- t else loop (Acc.accum dst h) t in let dummy = Acc.dummy () in loop dummy l; dummy.tl let remove_if f lst = let rec loop dst = function | [] -> () | x :: l -> if f x then dst.tl <- l else loop (Acc.accum dst x) l in let dummy = Acc.dummy () in loop dummy lst; dummy.tl let remove_all l x = let rec loop dst = function | [] -> () | h :: t -> if x = h then loop dst t else loop (Acc.accum dst h) t in let dummy = Acc.dummy () in loop dummy l; dummy.tl let transpose = function | [] -> [] | [x] -> List.map (fun x -> [x]) x | x::xs -> let heads = List.map Acc.create x in ignore ( fold_left (fun acc x -> map2 (fun x xs -> Acc.accum xs x) x acc) heads xs); Obj.magic heads (* equivalent to List.map inj heads, but without creating a new list *) (*$T transpose transpose [ [1; 2; 3;]; [4; 5; 6;]; [7; 8; 9;] ] = [[1;4;7];[2;5;8];[3;6;9]] transpose [] = [] transpose [ [1] ] = [ [1] ] *) let enum l = let rec make lr count = BatEnum.make ~next:(fun () -> match !lr with | [] -> raise BatEnum.No_more_elements | h :: t -> decr count; lr := t; h ) ~count:(fun () -> if !count < 0 then count := length !lr; !count ) ~clone:(fun () -> make (ref !lr) (ref !count) ) in make (ref l) (ref (-1)) let of_enum e = let h = Acc.dummy () in let _ = BatEnum.fold Acc.accum h e in h.tl let backwards l = enum (rev l) (*TODO: should we make it more efficient?*) (*let backwards l = (*This version only needs one pass but is actually less lazy*) let rec aux acc = function | [] -> acc | h::t -> aux BatEnum.append (BatEnum.singleton h) acc in aux l*) let of_backwards e = let rec aux acc = match BatEnum.get e with | Some h -> aux (h::acc) | None -> acc in aux [] let assoc_inv e l = let rec aux = function | [] -> raise Not_found | (a,b)::_ when b = e -> a | _::t -> aux t in aux l let assq_inv e l = let rec aux = function | [] -> raise Not_found | (a,b)::_ when b == e -> a | _::t -> aux t in aux l let modify_opt a f l = let rec aux p = function | [] -> (match f None with | None -> raise Exit | Some v -> rev ((a,v)::p)) | (a',b)::t when a' = a -> (match f (Some b) with | None -> rev_append p t | Some b' -> rev_append ((a,b')::p) t) | p'::t -> aux (p'::p) t in try aux [] l with Exit -> l (*$= modify_opt & ~printer:(IO.to_string (List.print (fun fmt (a,b) -> Printf.fprintf fmt "%d,%d" a b))) (* to modify a value *) \ (modify_opt 5 (function Some 1 -> Some 2 | _ -> assert false) [ 1,0 ; 5,1 ; 8,2 ]) \ [ 1,0 ; 5,2 ; 8,2 ] (* to add a value *) \ (modify_opt 5 (function None -> Some 2 | _ -> assert false) [ 1,0 ; 8,2 ]) \ [ 1,0 ; 8,2 ; 5,2 ] (* to remove a value *) \ (modify_opt 5 (function Some 1 -> None | _ -> assert false) [ 1,0 ; 5,1 ; 8,2 ]) \ [ 1,0 ; 8,2 ] *) let modify a f l = let f' = function | None -> raise Not_found | Some b -> Some (f b) in modify_opt a f' l (*$= modify & ~printer:(IO.to_string (List.print (fun fmt (a,b) -> Printf.fprintf fmt "%d,%d" a b))) (modify 5 succ [ 1,0 ; 5,1 ; 8,2 ]) [ 1,0 ; 5,2 ; 8,2 ] *) (*$T modify try ignore (modify 5 succ [ 1,0 ; 8,2 ]); false with Not_found -> true *) let modify_def dfl a f l = let f' = function | None -> Some (f dfl) | Some b -> Some (f b) in modify_opt a f' l (*$= modify_def & ~printer:(IO.to_string (List.print (fun fmt (a,b) -> Printf.fprintf fmt "%d,%d" a b))) (modify_def 0 5 succ [ 1,0 ; 5,1 ; 8,2 ]) [ 1,0 ; 5,2 ; 8,2 ] (modify_def 0 5 succ [ 1,0 ; 8,2 ]) [ 1,0 ; 8,2 ; 5,1 ] *) let modify_opt_at n f l = if n < 0 then invalid_arg at_negative_index_msg; let rec loop acc n = function | [] -> invalid_arg at_after_end_msg | h :: t -> if n <> 0 then loop (h :: acc) (n - 1) t else match f h with | None -> rev_append acc t | Some v -> rev_append acc (v :: t) in loop [] n l (*$T modify_opt_at modify_opt_at 2 (fun n -> Some (n*n)) [1;2;3;4;5] = [1;2;9;4;5] modify_opt_at 2 (fun _ -> None) [1;2;3;4;5] = [1;2;4;5] try ignore (modify_opt_at 0 (fun _ -> None) []); false \ with Invalid_argument _ -> true try ignore (modify_opt_at 2 (fun _ -> None) []); false \ with Invalid_argument _ -> true try ignore (modify_opt_at (-1) (fun _ -> None) [1;2;3]); false \ with Invalid_argument _ -> true try ignore (modify_opt_at 5 (fun _ -> None) [1;2;3]); false \ with Invalid_argument _ -> true try ignore (modify_opt_at 3 (fun _ -> None) [1;2;3]); false \ with Invalid_argument _ -> true *) let modify_at n f l = modify_opt_at n (fun x -> Some (f x)) l (*$T modify_at modify_at 2 ((+) 1) [1;2;3;4] = [1;2;4;4] try ignore (modify_at 0 ((+) 1) []); false \ with Invalid_argument _ -> true try ignore (modify_at 2 ((+) 1) []); false \ with Invalid_argument _ -> true try ignore (modify_at (-1) ((+) 1) [1;2;3]); false \ with Invalid_argument _ -> true try ignore (modify_at 5 ((+) 1) [1;2;3]); false \ with Invalid_argument _ -> true try ignore (modify_at 3 ((+) 1) [1;2;3]); false \ with Invalid_argument _ -> true *) let sort_unique cmp lst = let sorted = List.sort cmp lst in let fold first rest = List.fold_left (fun (acc, last) elem -> if (cmp last elem) = 0 then (acc, elem) else (elem::acc, elem) ) ([first], first) rest in match sorted with | [] -> [] | hd::tl -> begin let rev_result, _ = fold hd tl in List.rev rev_result end ##V<4.2##let sort_uniq = sort_unique ##V>=4.2##let sort_uniq = List.sort_uniq let group cmp lst = let sorted = List.sort cmp lst in let fold first rest = List.fold_left (fun (acc, agr, last) elem -> if (cmp last elem) = 0 then (acc, elem::agr, elem) else (agr::acc, [elem], elem) ) ([], [first], first) rest in match sorted with | [] -> [] | hd::tl -> begin let groups, lastgr, _ = fold hd tl in List.rev_map List.rev (lastgr::groups) end (*$T group group BatInt.compare [] = [] group BatInt.compare [1] = [[1]] group BatInt.compare [2; 2] = [[2; 2]] group BatInt.compare [5; 4; 4; 2; 1; 6] = [[1]; [2]; [4; 4]; [5]; [6]] *) let cartesian_product l1 l2 = List.concat (List.map (fun i -> List.map (fun j -> (i,j)) l2) l1) (*$T cartesian_product as cp cp [1;2;3] ['x';'y'] = [1,'x';1,'y';2,'x';2,'y';3,'x';3,'y'] *) let rec n_cartesian_product = function | [] -> [[]] | h :: t -> let rest = n_cartesian_product t in List.concat (List.map (fun i -> List.map (fun r -> i :: r) rest) h) (*$T n_cartesian_product as ncp ncp [] = [[]] ncp [[]] = [] ncp [[1]; [2]; [3]] = [[1;2;3]] ncp [[1;2;3]] = [[1]; [2]; [3]] ncp [[1;2;3]; []] = [] ncp [[1;2;3]; [4;5]] = [[1;4]; [1;5]; [2;4]; [2;5]; [3;4]; [3;5]] *) let print ?(first="[") ?(last="]") ?(sep="; ") print_a out = function | [] -> BatInnerIO.nwrite out first; BatInnerIO.nwrite out last | [h] -> BatInnerIO.nwrite out first; print_a out h; BatInnerIO.nwrite out last | h::t -> BatInnerIO.nwrite out first; print_a out h; iter (fun x -> BatInnerIO.nwrite out sep; print_a out x) t; BatInnerIO.nwrite out last let t_printer a_printer _paren out x = print (a_printer false) out x let reduce f = function | [] -> invalid_arg "List.reduce: Empty List" | h :: t -> fold_left f h t let min ?cmp:(cmp = Pervasives.compare) l = let min = BatOrd.min_comp cmp in reduce min l let max ?cmp:(cmp = Pervasives.compare) l = let max = BatOrd.max_comp cmp in reduce max l let sum l = fold_left (+) 0 l (*$= sum & ~printer:string_of_int 2 (sum [1;1]) 0 (sum []) *) let fsum l = match l with | [] -> 0. | x::xs -> let acc = ref x in let rem = ref xs in let go = ref true in while !go do match !rem with | [] -> go := false; | x::xs -> acc := !acc +. x; rem := xs done; !acc (*$= fsum & ~printer:string_of_float 0. (fsum []) 6. (fsum [1.;2.;3.]) *) let favg l = match l with | [] -> invalid_arg "List.favg: Empty List" | x::xs -> let acc = ref x in let len = ref 1 in let rem = ref xs in let go = ref true in while !go do match !rem with | [] -> go := false; | x::xs -> acc := !acc +. x; incr len; rem := xs done; !acc /. float_of_int !len (*$T favg try let _ = favg [] in false with Invalid_argument _ -> true favg [1.;2.;3.] = 2. *) let kahan_sum li = (* This algorithm is written in a particularly untasteful imperative style to benefit from the nice unboxing of float references that is harder to obtain with recursive functions today. See the definition of kahan sum on arrays, on which this one is directly modeled. *) let li = ref li in let continue = ref (!li <> []) in let sum = ref 0. in let err = ref 0. in while !continue do match !li with | [] -> continue := false | x::xs -> li := xs; let x = x -. !err in let new_sum = !sum +. x in err := (new_sum -. !sum) -. x; sum := new_sum +. 0.; done; !sum +. 0. (*$T kahan_sum kahan_sum [ ] = 0. kahan_sum [ 1.; 2. ] = 3. let n, x = 1_000, 1.1 in \ Float.approx_equal (float n *. x) \ (kahan_sum (List.make n x)) *) let min_max ?cmp:(cmp = Pervasives.compare) = function | [] -> invalid_arg "List.min_max: Empty List" | x :: xs -> fold_left (fun (curr_min, curr_max) y -> let new_min = if cmp curr_min y = 1 then y else curr_min in let new_max = if cmp curr_max y = -1 then y else curr_max in (new_min, new_max) ) (x, x) xs (*$T min_max min_max [1] = (1, 1) min_max [1; 1] = (1, 1) min_max [1; -2; 3; 4; 5; 60; 7; 8] = (-2, 60) *) let unfold b f = let acc = Acc.dummy () in let rec loop dst v = match f v with | None -> acc.tl | Some (a, v) -> loop (Acc.accum dst a) v in loop acc b (*$T unfold unfold 1 (fun x -> None) = [] unfold 0 (fun x -> if x > 3 then None else Some (x, succ x)) = [0;1;2;3] *) let subset cmp l l' = for_all (fun x -> mem_cmp cmp x l') l (*$T subset subset BatInt.compare [1;2;3;4] [1;2;3] = false subset BatInt.compare [1;2;3] [1;2;3] = true subset BatInt.compare [3;2;1] [1;2;3] = true subset BatInt.compare [1;2] [1;2;3] = true *) let shuffle ?state l = let arr = Array.of_list l in BatInnerShuffle.array_shuffle ?state arr; Array.to_list arr (*$T shuffle let s = Random.State.make [|11|] in \ shuffle ~state:s [1;2;3;4;5;6;7;8;9] = \ let ocaml_version = int_of_string (String.make 1 Sys.ocaml_version.[0]) in \ if ocaml_version < 5 then \ [7; 2; 9; 5; 3; 6; 4; 1; 8] else \ [1; 7; 4; 9; 5; 2; 8; 6; 3] shuffle [] = [] *) module Exceptionless = struct let rfind p l = try Some (rfind p l) with Not_found -> None let find p l = try Some (find p l) with Not_found -> None let findi p l = try Some (findi p l) with Not_found -> None let split_at n l = try `Ok (split_at n l) with Invalid_argument s -> `Invalid_argument s let at n l = try `Ok (at n l) with Invalid_argument s -> `Invalid_argument s let assoc e l = try Some (assoc e l) with Not_found -> None let assq e l = try Some (assq e l) with Not_found -> None let assoc_inv e l = try Some (assoc_inv e l) with Not_found -> None let find_map f l = try Some(find_map f l) with Not_found -> None let hd l = try Some (hd l) with Failure _ -> None let tl l = try Some (tl l) with Failure _ -> None let rec last = function | [] -> None | [x] -> Some x | _ :: l -> last l let reduce f = function | [] -> None | h :: t -> Some (fold_left f h t) let min_max ?cmp:(cmp = Pervasives.compare) l = try Some (min_max ~cmp l) with Invalid_argument _ -> None let min ?cmp:(cmp = Pervasives.compare) l = try Some (min ~cmp l) with Invalid_argument _ -> None let max ?cmp:(cmp = Pervasives.compare) l = try Some (max ~cmp l) with Invalid_argument _ -> None end module Labels = struct let init i ~f = init i f let make n x = make n x let iteri ~f l = iteri f l let map ~f l = map f l let mapi ~f l = mapi f l let rfind ~f l = rfind f l let find ~f l = find f l let findi ~f = findi f let find_exn ~f = find_exn f ##V>=4.10##let find_map_opt ~f = find_map_opt f let filter_map ~f = filter_map f let remove_if ~f = remove_if f let take_while ~f = take_while f let drop_while ~f = drop_while f let map2 ~f = map2 f let iter2 ~f = iter2 f let exists2 ~f = exists2 f let fold_left ~f ~init = fold_left f init let fold = fold_left let fold_right ~f l ~init = fold_right f l init let fold_left2 ~f ~init = fold_left2 f init let fold_right2 ~f l1 l2 ~init = fold_right2 f l1 l2 init let filter ~f = filter f let count_matching ~f = count_matching f ##V>=4.10##let concat_map ~f = List.concat_map f let find_all ~f = find_all f let partition ~f = partition f let partition_map ~f = partition_map f let rev_map ~f = rev_map f let rev_map2 ~f = rev_map2 f let iter ~f = iter f let for_all ~f = for_all f let for_all2 ~f = for_all2 f let exists ~f = exists f let subset ~cmp = subset cmp let stable_sort ?(cmp=compare) = stable_sort cmp let fast_sort ?(cmp=compare) = fast_sort cmp let sort ?(cmp=compare) = sort cmp let merge ?(cmp=compare) = merge cmp module LExceptionless = struct include Exceptionless let rfind ~f l = rfind f l let find ~f l = find f l let findi ~f l = findi f l end end let ( @ ) = List.append module Infix = struct let ( @ ) = ( @ ) end open BatOrd let rec eq eq_elt l1 l2 = match l1 with | [] -> (match l2 with [] -> true | _ -> false) | hd1::tl1 -> (match l2 with | [] -> false | hd2::tl2 -> bin_eq eq_elt hd1 hd2 (eq eq_elt) tl1 tl2) let rec ord ord_elt l1 l2 = match l1 with | [] -> (match l2 with [] -> Eq | _::_ -> Lt) | hd1::tl1 -> (match l2 with | [] -> Gt | hd2::tl2 -> bin_ord ord_elt hd1 hd2 (ord ord_elt) tl1 tl2) let rec compare comp_elt l1 l2 = match l1 with | [] -> (match l2 with [] -> 0 | _::_ -> -1) | hd1::tl1 -> (match l2 with | [] -> 1 | hd2::tl2 -> bin_comp comp_elt hd1 hd2 (compare comp_elt) tl1 tl2) module Eq (T : Eq) = struct type t = T.t list let eq = eq T.eq end module Ord (T : Ord) = struct type t = T.t list let ord = ord T.ord end module Comp (T : Comp) = struct type t = T.t list let compare = compare T.compare end