123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program 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 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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 program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(**
ListExt - Adds a few useful functions to OCaml lists
*)includeList(** Import everything from List *)(** {2 Operations} *)letreclast=function|[]->invalid_arg"listUtil.last: empty list"|[a]->a|a::b->lastb(** Last eleemnt of a list. Raises invalid_arg if the list is empty. *)letmap_tailfl=rev(rev_mapfl)(** Tail-recursive version ofmap. Useful for large lists. *)letappend_tailab=rev_append(List.reva)b(** Tail-recursive version of append. Useful if the first list is large.. *)letmap_merge(f:'a->'blist)(l:'alist):'blist=List.rev(List.fold_left(funaccx->List.rev_append(fx)acc)[]l)(** Maps [e1;e2;...;en] to (f e1)@(f e2)@...@(f en) *)letmap_filter(f:'a->'boption)(l:'alist):'blist=letrecdoitaccl=matchlwith|[]->revacc|a::r->doit(matchfawithSomex->x::acc|None->acc)rindoit[]l(** Applies f to every element of the list and kepp only those that return some value.
The order of the argument list is preserved.
*)letsplit(l:'alist):'alist*'alist=letrecdoitacclnb=ifnb<=0then(List.revacc),lelsedoit((List.hdl)::acc)(List.tll)(nb-1)indoit[]l((List.lengthl+1)/2)(** [split l] cuts the list [l] into two halves.
Either the two halves have the same size, or the first list is
one element larger.
If [a,b = split l], then [l = a @ b].
*)(** {2 Printing} *)typelist_printer={print_empty:string;(** Special text for empty lists *)print_begin:string;(** Text before the first element. *)print_sep:string;(** Text between two elements *)print_end:string;(** Text after the last element *)}(** Tells how to print a list. *)letprinter_plain={print_empty="";print_begin="";print_sep=" ";print_end="";}(** Print as a space-sparated list, no delimiters. *)letprinter_list={print_empty="[]";print_begin="[";print_sep=";";print_end="]";}(** Print as OCaml list: [a;b;c]. *)letprinter_tuple={print_empty="()";print_begin="(";print_sep=",";print_end=")";}(** Print as OCaml tuple: (a,b,c). *)letprinter_set={print_empty="{}";print_begin="{";print_sep=";";print_end="}";}(** Print as set: {a;b;c}. *)letprint_genoprinterelemchl=matchlwith|[]->ochprinter.print_empty|a::rest->ochprinter.print_begin;elemcha;List.iter(fune->ochprinter.print_sep;elemche)rest;ochprinter.print_end(* internal printing helper *)letprintprinterelemchl=print_genoutput_stringprinterelemchlletbprintprinterelemchl=print_genBuffer.add_stringprinterelemchlletfprintprinterelemchl=print_gen(funfmts->Format.fprintffmt"%s@,"s)printerelemchlletto_stringprintereleml=letb=Buffer.create10inprint_gen(fun()s->Buffer.add_stringbs)printer(fun()e->eleme)()l;Buffer.contentsbletreccomparecmpab=matcha,bwith|[],[]->0|[],_->1|_,[]->-1|x::s,y::t->letr=cmpxyinifr=0thencomparecmpstelserletrecmem_comparecmpel=matchlwith|p::qwhencmppe=0->true|p::q->mem_comparecmpeq|[]->false(** {2 Parallel functions} *)(*
Disabled for now.
Not used, yet causes compiler warnings about threading...
let par_iteri (nb_threads:int) (f: int -> 'a -> unit) (l:'a list) : unit =
if nb_threads <= 1 || List.length l <= 1
then List.iteri f l
else
let exn = ref None in (* exception thrown in thread *)
let mtx = Mutex.create () in
let i = ref 0 in
let ll = ref l in
let rec consumer () =
Mutex.lock mtx;
match !ll with
| a::b when !exn = None->
(* eat one *)
let err = ref false in
ll := b;
let ii = !i in
incr i;
Mutex.unlock mtx;
(try
f ii a
with x ->
(* remember exception for main thread *)
Mutex.lock mtx;
exn := Some x;
Mutex.unlock mtx;
err := true
);
if not !err then consumer ()
| _ ->
(* the end *)
Mutex.unlock mtx
in
Array.init
(min nb_threads (List.length l))
(fun _ -> Thread.create consumer ())
|> Array.iter Thread.join;
match !exn with
| None -> ()
| Some x -> raise x (* rethrow exception from thread *)
*)(**
As List.iter, but in parallel using nb_threads threads.
As threads are used, this only makes sense if the iterated function
calls a C function that temporarily lifts the global interpreter lock
(e.g., the Clang parser)
*)