| _ :: tl -> list_assoc_all a tl
;;
+let rec rm_assoc n = function
+ | [] -> assert false
+ | (x,i)::tl when n=x -> i,tl
+ | p::tl -> let i,tl = rm_assoc n tl in i,p::tl
+;;
+
+(* naif implementation of the union-find merge operation
+ canonicals maps elements to canonicals
+ elements maps canonicals to the classes *)
+let merge canonicals elements n m =
+ let cn,canonicals = rm_assoc n canonicals in
+ let cm,canonicals = rm_assoc m canonicals in
+ let ln,elements = rm_assoc cn elements in
+ let lm,elements = rm_assoc cm elements in
+ let canonicals =
+ (n,cm)::(m,cm)::List.map
+ (fun ((x,xc) as p) ->
+ if xc = cn then (x,cm) else p) canonicals
+ in
+ let elements = (cm,ln@lm)::elements
+ in
+ canonicals,elements
+;;
+
+(* f x gives the direct dependencies of x;
+ x must not belong to (f x) and (f x) must
+ be a subset of l *)
+let clusters f l =
+ let canonicals = List.map (fun x -> (x,x)) l in
+ let elements = List.map (fun x -> (x,[x])) l in
+ let _,elements =
+ List.fold_left
+ (fun (canonicals,elements) x ->
+ let dep = f x in
+ List.fold_left
+ (fun (canonicals,elements) d ->
+ merge canonicals elements d x)
+ (canonicals,elements) dep)
+ (canonicals,elements) l
+ in
+ List.map snd elements
+;;
+
(** {2 File predicates} *)
let is_dir fname =
(* like List.assoc but returns all bindings *)
val list_assoc_all: 'a -> ('a * 'b) list -> 'b list
+val rm_assoc : 'a -> ('a * 'b) list -> 'b * ('a * 'b) list
+
+val clusters : ('a -> 'a list) -> 'a list -> 'a list list
(** {2 Debugging & Profiling} *)
type profiler = { profile : 'a 'b. ('a -> 'b) -> 'a -> 'b }