Raw File
list.ml
(*****************************************************************************)
(*                                                                           *)
(* MIT License                                                               *)
(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com>                *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

include Stdlib.List
module List = Stdlib.List

(* Separate the heads and the tails of a list of list.
   raises if one of the lists is empty *)
let get_heads_and_tails ll =
  let rec aux heads tails = function
    | [] -> (rev heads, rev tails)
    | (h :: t) :: ll -> aux (h :: heads) (t :: tails) ll
    | _ -> raise (Invalid_argument "get_firsts_and_tails: one list is empty")
  in
  aux [] [] ll

(* map for n lists of the same size grouped in ln *)
(* the use of get_firsts_and_tails ensures all lists in ln have the same size *)
let mapn f = function
  | [] -> []
  | ln ->
      let rec aux ln =
        match hd ln with
        | [] -> []
        | _ ->
            let heads, tails = get_heads_and_tails ln in
            f heads :: aux tails
      in
      aux ln

(* List.fold_left2 which stops when end of one of the lists is reached *)
let rec fold_left2_opt f acc l1 l2 =
  match (l1, l2) with
  | [], _ -> acc
  | _, [] -> acc
  | a1 :: l1, a2 :: l2 -> fold_left2_opt f (f acc a1 a2) l1 l2

(* List.fold_left for 3 lists of same size *)
let rec fold_left3 f acc l1 l2 l3 =
  match (l1, l2, l3) with
  | [], [], [] -> acc
  | a1 :: l1, a2 :: l2, a3 :: l3 -> fold_left3 f (f acc a1 a2 a3) l1 l2 l3
  | _ -> raise (Invalid_argument "fold_left3 : lists don’t have the same size.")

(* List.fold_left for 4 lists of same size *)
let rec fold_left4 f acc l1 l2 l3 l4 =
  match (l1, l2, l3, l4) with
  | [], [], [], [] -> acc
  | a1 :: l1, a2 :: l2, a3 :: l3, a4 :: l4 ->
      fold_left4 f (f acc a1 a2 a3 a4) l1 l2 l3 l4
  | _ -> raise (Invalid_argument "fold_left4 : lists don’t have the same size.")

(* List.fold_left for 5 lists of same size *)
let rec fold_left5 f acc l1 l2 l3 l4 l5 =
  match (l1, l2, l3, l4, l5) with
  | [], [], [], [], [] -> acc
  | a1 :: l1, a2 :: l2, a3 :: l3, a4 :: l4, a5 :: l5 ->
      fold_left5 f (f acc a1 a2 a3 a4 a5) l1 l2 l3 l4 l5
  | _ -> raise (Invalid_argument "fold_left5 : lists don’t have the same size.")

(* List.fold_left for 6 lists of same size *)
let rec fold_left6 f acc l1 l2 l3 l4 l5 l6 =
  match (l1, l2, l3, l4, l5, l6) with
  | [], [], [], [], [], [] -> acc
  | a1 :: l1, a2 :: l2, a3 :: l3, a4 :: l4, a5 :: l5, a6 :: l6 ->
      fold_left6 f (f acc a1 a2 a3 a4 a5 a6) l1 l2 l3 l4 l5 l6
  | _ -> raise (Invalid_argument "fold_left6 : lists don’t have the same size.")

let split_n n l =
  let rec aux acc k l =
    if k = n then (List.rev acc, l)
    else
      match l with
      | h :: t -> aux (h :: acc) (k + 1) t
      | [] ->
          raise
            (Invalid_argument
               (Printf.sprintf "split_n: n=%d >= List.length l=%d" n k))
  in
  aux [] 0 l

let split_in_half l =
  let len = List.length l in
  match len mod 2 with
  | 0 -> split_n (len / 2) l
  | _ ->
      raise
        (Invalid_argument
           (Printf.sprintf "split_in_half: length %d not even." len))

let map f l = rev (rev_map f l)

let map2 f l1 l2 = rev (rev_map2 f l1 l2)

let rev_mapi f l =
  let rec rmap_f i accu = function
    | [] -> accu
    | a :: l -> rmap_f (i + 1) (f i a :: accu) l
  in
  rmap_f 0 [] l

let mapi f l = rev (rev_mapi f l)

(* not tail-recursive *)
let rec map2_opt merge l1 l2 =
  match (l1, l2) with
  | [], _ -> l2
  | _, [] -> l1
  | h1 :: t1, h2 :: t2 -> merge h1 h2 :: map2_opt merge t1 t2

(* same as List.combine but allows lists of different sizes *)
let safe_combine l1 l2 =
  let rec aux acc l1 l2 =
    match (l1, l2) with
    | [], _ -> List.rev acc
    | _, [] -> List.rev acc
    | h1 :: t1, h2 :: t2 -> aux ((h1, h2) :: acc) t1 t2
  in
  aux [] l1 l2

exception Internal

(* equivalent to List.rev (List.flatten l) but tail recursive*)
let rev_flatten l =
  let rec aux res l =
    match l with [] -> res | h :: t -> aux (List.rev_append h res) t
  in
  aux [] l

let flatten l = List.rev @@ rev_flatten l

(* applies a random permutation to the elements of the given list *)
let shuffle ?seed l =
  (match seed with None -> () | Some i -> Random.init i) ;
  List.rev_map (fun x -> (Random.bits (), x)) l
  |> List.sort (fun (i, _) (j, _) -> Int.compare i j)
  |> List.rev_map snd

let sub l start len = List.filteri (fun i _ -> start <= i && i < start + len) l

(* Applies the function [f] to the last element of the list [l] ; if [l] is empty, it returns the empty list. *)
let map_end f l =
  let rec aux acc = function
    | [] -> []
    | [x] -> rev (f x :: acc)
    | x :: tl -> aux (x :: acc) tl
  in
  aux [] l
back to top