mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
map, set, and IO/string adapters for CCLinq;
also a lazy constructor
This commit is contained in:
parent
4550a1c2c2
commit
c021a2b310
2 changed files with 349 additions and 104 deletions
331
core/CCLinq.ml
331
core/CCLinq.ml
|
|
@ -35,7 +35,13 @@ type 'a hash = 'a -> int
|
|||
|
||||
let _id x = x
|
||||
|
||||
module Map = struct
|
||||
type 'a collection =
|
||||
| Seq : 'a sequence -> 'a collection
|
||||
| List : 'a list -> 'a collection
|
||||
| Set : (module CCSequence.Set.S
|
||||
with type elt = 'a and type t = 'b) * 'b -> 'a collection
|
||||
|
||||
module PMap = struct
|
||||
type ('a, 'b) t = {
|
||||
is_empty : unit -> bool;
|
||||
size : unit -> int; (** Number of keys *)
|
||||
|
|
@ -49,6 +55,7 @@ module Map = struct
|
|||
| None -> false
|
||||
| Some _ -> true
|
||||
let to_seq m = m.to_seq
|
||||
let fold f acc m = m.fold f acc
|
||||
let size m = m.size ()
|
||||
|
||||
type ('a, 'b) build = {
|
||||
|
|
@ -144,7 +151,39 @@ module Map = struct
|
|||
| None -> raise Not_found
|
||||
| Some x -> x
|
||||
|
||||
(* map values *)
|
||||
let map f m = {
|
||||
is_empty = m.is_empty;
|
||||
size = m.size;
|
||||
get = (fun k -> match m.get k with
|
||||
| None -> None
|
||||
| Some v -> Some (f v)
|
||||
);
|
||||
to_seq = CCSequence.map (fun (x,y) -> x, f y) m.to_seq;
|
||||
fold = (fun f' acc ->
|
||||
m.fold (fun acc x y -> f' acc x (f y)) acc
|
||||
);
|
||||
}
|
||||
|
||||
let to_list m = m.to_seq |> CCSequence.to_rev_list
|
||||
|
||||
let to_coll m = Seq m.to_seq
|
||||
|
||||
let reverse ~build m =
|
||||
let build = make ~build () in
|
||||
to_seq m
|
||||
|> CCSequence.map (fun (x,y) -> y,x)
|
||||
|> multimap_of_seq ~build
|
||||
|
||||
let reverse_multimap ~build m =
|
||||
let build = make ~build () in
|
||||
to_seq m
|
||||
|> CCSequence.flatMap
|
||||
(fun (x,l) ->
|
||||
CCSequence.of_list l
|
||||
|> CCSequence.map (fun y -> y,x)
|
||||
)
|
||||
|> multimap_of_seq ~build
|
||||
end
|
||||
|
||||
type 'a search_result =
|
||||
|
|
@ -155,21 +194,15 @@ type ('a,'b,'key,'c) join_descr = {
|
|||
join_key1 : 'a -> 'key;
|
||||
join_key2 : 'b -> 'key;
|
||||
join_merge : 'key -> 'a -> 'b -> 'c option;
|
||||
join_build : 'key Map.build_method;
|
||||
join_build : 'key PMap.build_method;
|
||||
}
|
||||
|
||||
type ('a,'b) group_join_descr = {
|
||||
gjoin_proj : 'b -> 'a;
|
||||
gjoin_build : 'a Map.build_method;
|
||||
gjoin_build : 'a PMap.build_method;
|
||||
}
|
||||
|
||||
module Coll = struct
|
||||
type 'a t =
|
||||
| Seq : 'a sequence -> 'a t
|
||||
| List : 'a list -> 'a t
|
||||
| Set : (module CCSequence.Set.S
|
||||
with type elt = 'a and type t = 'b) * 'b -> 'a t
|
||||
|
||||
let of_seq s = Seq s
|
||||
let of_list l = List l
|
||||
let of_array a = Seq (CCSequence.of_array a)
|
||||
|
|
@ -299,12 +332,12 @@ module Coll = struct
|
|||
let build1 =
|
||||
to_seq c1
|
||||
|> CCSequence.map (fun x -> join.join_key1 x, x)
|
||||
|> Map.multimap_of_seq ~build:(Map.make ~build:join.join_build ())
|
||||
|> PMap.multimap_of_seq ~build:(PMap.make ~build:join.join_build ())
|
||||
in
|
||||
let l = CCSequence.fold
|
||||
(fun acc y ->
|
||||
let key = join.join_key2 y in
|
||||
match Map.get build1 key with
|
||||
match PMap.get build1 key with
|
||||
| None -> acc
|
||||
| Some l1 ->
|
||||
List.fold_left
|
||||
|
|
@ -317,29 +350,29 @@ module Coll = struct
|
|||
of_list l
|
||||
|
||||
let do_group_join ~gjoin c1 c2 =
|
||||
let build = Map.make ~build:gjoin.gjoin_build () in
|
||||
to_seq c1 (fun x -> Map.add build x []);
|
||||
let build = PMap.make ~build:gjoin.gjoin_build () in
|
||||
to_seq c1 (fun x -> PMap.add build x []);
|
||||
to_seq c2
|
||||
(fun y ->
|
||||
(* project [y] into some element of [c1] *)
|
||||
let x = gjoin.gjoin_proj y in
|
||||
Map.update build x
|
||||
PMap.update build x
|
||||
(function
|
||||
| None -> None (* [x] not present, ignore! *)
|
||||
| Some l -> Some (y::l)
|
||||
)
|
||||
);
|
||||
Map.build_get build
|
||||
PMap.build_get build
|
||||
|
||||
let do_product c1 c2 =
|
||||
let s1 = to_seq c1 and s2 = to_seq c2 in
|
||||
of_seq (CCSequence.product s1 s2)
|
||||
|
||||
let do_union ~build c1 c2 =
|
||||
let build = Map.make ~build () in
|
||||
to_seq c1 (fun x -> Map.add build x ());
|
||||
to_seq c2 (fun x -> Map.add build x ());
|
||||
Map.to_seq (Map.build_get build)
|
||||
let build = PMap.make ~build () in
|
||||
to_seq c1 (fun x -> PMap.add build x ());
|
||||
to_seq c2 (fun x -> PMap.add build x ());
|
||||
PMap.to_seq (PMap.build_get build)
|
||||
|> CCSequence.map fst
|
||||
|> of_seq
|
||||
|
||||
|
|
@ -348,11 +381,11 @@ module Coll = struct
|
|||
| InterDone (* already output *)
|
||||
|
||||
let do_inter ~build c1 c2 =
|
||||
let build = Map.make ~build () in
|
||||
let build = PMap.make ~build () in
|
||||
let l = ref [] in
|
||||
to_seq c1 (fun x -> Map.add build x InterLeft);
|
||||
to_seq c1 (fun x -> PMap.add build x InterLeft);
|
||||
to_seq c2 (fun x ->
|
||||
Map.update build x
|
||||
PMap.update build x
|
||||
(function
|
||||
| None -> Some InterDone
|
||||
| Some InterDone as foo -> foo
|
||||
|
|
@ -364,17 +397,15 @@ module Coll = struct
|
|||
of_list !l
|
||||
|
||||
let do_diff ~build c1 c2 =
|
||||
let build = Map.make ~build () in
|
||||
to_seq c2 (fun x -> Map.add build x ());
|
||||
let map = Map.build_get build in
|
||||
let build = PMap.make ~build () in
|
||||
to_seq c2 (fun x -> PMap.add build x ());
|
||||
let map = PMap.build_get build in
|
||||
(* output elements of [c1] not in [map] *)
|
||||
to_seq c1
|
||||
|> CCSequence.filter (fun x -> not (Map.mem map x))
|
||||
|> CCSequence.filter (fun x -> not (PMap.mem map x))
|
||||
|> of_seq
|
||||
end
|
||||
|
||||
type 'a collection = 'a Coll.t
|
||||
|
||||
(** {2 Query operators} *)
|
||||
|
||||
type (_,_) safety =
|
||||
|
|
@ -382,10 +413,12 @@ type (_,_) safety =
|
|||
| Unsafe : ('a, 'a) safety
|
||||
|
||||
type (_, _) unary =
|
||||
| Map : ('a -> 'b) -> ('a collection, 'b collection) unary
|
||||
| PMap : ('a -> 'b) -> ('a collection, 'b collection) unary
|
||||
| GeneralMap : ('a -> 'b) -> ('a, 'b) unary
|
||||
| Filter : ('a -> bool) -> ('a collection, 'a collection) unary
|
||||
| Fold : ('b -> 'a -> 'b) * 'b -> ('a collection, 'b) unary
|
||||
| FoldMap : ('acc -> 'a -> 'b -> 'acc) * 'acc
|
||||
-> (('a,'b) PMap.t, 'acc) unary
|
||||
| Reduce : ('c, 'd) safety * ('a -> 'b) * ('a -> 'b -> 'b) * ('b -> 'c)
|
||||
-> ('a collection, 'd) unary
|
||||
| Size : ('a collection, int) unary
|
||||
|
|
@ -401,10 +434,11 @@ type (_, _) unary =
|
|||
failure : 'b;
|
||||
> -> ('a collection, 'b) unary
|
||||
| Contains : 'a equal * 'a -> ('a collection, bool) unary
|
||||
| Get : ('b,'c) safety * 'a -> (('a,'b) Map.t, 'c) unary
|
||||
| GroupBy : 'b Map.build_method * ('a -> 'b)
|
||||
-> ('a collection, ('b,'a list) Map.t) unary
|
||||
| Count : 'a Map.build_method -> ('a collection, ('a, int) Map.t) unary
|
||||
| Get : ('b,'c) safety * 'a -> (('a,'b) PMap.t, 'c) unary
|
||||
| GroupBy : 'b PMap.build_method * ('a -> 'b)
|
||||
-> ('a collection, ('b,'a list) PMap.t) unary
|
||||
| Count : 'a PMap.build_method -> ('a collection, ('a, int) PMap.t) unary
|
||||
| Lazy : ('a lazy_t, 'a) unary
|
||||
|
||||
type set_op =
|
||||
| Union
|
||||
|
|
@ -415,10 +449,10 @@ type (_, _, _) binary =
|
|||
| Join : ('a, 'b, 'key, 'c) join_descr
|
||||
-> ('a collection, 'b collection, 'c collection) binary
|
||||
| GroupJoin : ('a, 'b) group_join_descr
|
||||
-> ('a collection, 'b collection, ('a, 'b list) Map.t) binary
|
||||
-> ('a collection, 'b collection, ('a, 'b list) PMap.t) binary
|
||||
| Product : ('a collection, 'b collection, ('a*'b) collection) binary
|
||||
| Append : ('a collection, 'a collection, 'a collection) binary
|
||||
| SetOp : set_op * 'a Map.build_method
|
||||
| SetOp : set_op * 'a PMap.build_method
|
||||
-> ('a collection, 'a collection, 'a collection) binary
|
||||
|
||||
(* type of queries that return a 'a *)
|
||||
|
|
@ -431,18 +465,30 @@ and 'a t =
|
|||
|
||||
let start x = Start x
|
||||
|
||||
let start_list l =
|
||||
let of_list l =
|
||||
Start (Coll.of_list l)
|
||||
|
||||
let start_array a =
|
||||
let of_array a =
|
||||
Start (Coll.of_array a)
|
||||
|
||||
let start_hashtbl h =
|
||||
let of_array_i a =
|
||||
Start (CCSequence.of_array_i a |> Coll.of_seq)
|
||||
|
||||
let of_hashtbl h =
|
||||
Start (Coll.of_seq (CCSequence.of_hashtbl h))
|
||||
|
||||
let start_seq seq =
|
||||
let of_seq seq =
|
||||
Start (Coll.of_seq seq)
|
||||
|
||||
let of_queue q =
|
||||
Start (CCSequence.of_queue q |> Coll.of_seq)
|
||||
|
||||
let of_stack s =
|
||||
Start (CCSequence.of_stack s |> Coll.of_seq)
|
||||
|
||||
let of_string s =
|
||||
Start (CCSequence.of_str s |> Coll.of_seq)
|
||||
|
||||
(** {6 Execution} *)
|
||||
|
||||
let rec _optimize : type a. a t -> a t
|
||||
|
|
@ -456,32 +502,32 @@ let rec _optimize : type a. a t -> a t
|
|||
| Bind _ -> q (* cannot optimize before execution *)
|
||||
and _optimize_unary : type a b. (a,b) unary -> a t -> b t
|
||||
= fun u q -> match u, q with
|
||||
| Map f, Unary (Map g, q') ->
|
||||
_optimize_unary (Map (fun x -> f (g x))) q'
|
||||
| Filter p, Unary (Map f, cont) ->
|
||||
| PMap f, Unary (PMap g, q') ->
|
||||
_optimize_unary (PMap (fun x -> f (g x))) q'
|
||||
| Filter p, Unary (PMap f, cont) ->
|
||||
_optimize_unary
|
||||
(FilterMap (fun x -> let y = f x in if p y then Some y else None))
|
||||
cont
|
||||
| Map f, Unary (Filter p, cont) ->
|
||||
| PMap f, Unary (Filter p, cont) ->
|
||||
_optimize_unary
|
||||
(FilterMap (fun x -> if p x then Some (f x) else None))
|
||||
cont
|
||||
| Map f, Binary (Append, q1, q2) ->
|
||||
| PMap f, Binary (Append, q1, q2) ->
|
||||
_optimize_binary Append (Unary (u, q1)) (Unary (u, q2))
|
||||
| Filter p, Binary (Append, q1, q2) ->
|
||||
_optimize_binary Append (Unary (u, q1)) (Unary (u, q2))
|
||||
| Fold (f,acc), Unary (Map f', cont) ->
|
||||
| Fold (f,acc), Unary (PMap f', cont) ->
|
||||
_optimize_unary
|
||||
(Fold ((fun acc x -> f acc (f' x)), acc))
|
||||
cont
|
||||
| Reduce (safety, start, mix, stop), Unary (Map f, cont) ->
|
||||
| Reduce (safety, start, mix, stop), Unary (PMap f, cont) ->
|
||||
_optimize_unary
|
||||
(Reduce (safety,
|
||||
(fun x -> start (f x)),
|
||||
(fun x acc -> mix (f x) acc),
|
||||
stop))
|
||||
cont
|
||||
| Size, Unary (Map _, cont) ->
|
||||
| Size, Unary (PMap _, cont) ->
|
||||
_optimize_unary Size cont (* ignore the map! *)
|
||||
| Size, Unary (Sort _, cont) ->
|
||||
_optimize_unary Size cont
|
||||
|
|
@ -494,10 +540,11 @@ and _optimize_binary : type a b c. (a,b,c) binary -> a t -> b t -> c t
|
|||
(* apply a unary operator on a collection *)
|
||||
let _do_unary : type a b. (a,b) unary -> a -> b
|
||||
= fun u c -> match u with
|
||||
| Map f -> Coll.map f c
|
||||
| PMap f -> Coll.map f c
|
||||
| GeneralMap f -> f c
|
||||
| Filter p -> Coll.filter p c
|
||||
| Fold (f, acc) -> Coll.fold f acc c
|
||||
| FoldMap (f, acc) -> PMap.fold f acc c
|
||||
| Reduce (safety, start, mix, stop) ->
|
||||
let acc = Coll.to_seq c
|
||||
|> CCSequence.fold
|
||||
|
|
@ -526,16 +573,17 @@ let _do_unary : type a b. (a,b) unary -> a -> b
|
|||
| Sort cmp -> Coll.sort cmp c
|
||||
| Distinct cmp -> Coll.distinct ~cmp c
|
||||
| Search obj -> Coll.search obj c
|
||||
| Get (Safe, k) -> Map.get c k
|
||||
| Get (Unsafe, k) -> Map.get_exn c k
|
||||
| Get (Safe, k) -> PMap.get c k
|
||||
| Get (Unsafe, k) -> PMap.get_exn c k
|
||||
| GroupBy (build,f) ->
|
||||
Coll.to_seq c
|
||||
|> CCSequence.map (fun x -> f x, x)
|
||||
|> Map.multimap_of_seq ~build:(Map.make ~build ())
|
||||
|> PMap.multimap_of_seq ~build:(PMap.make ~build ())
|
||||
| Contains (eq, x) -> Coll.contains ~eq x c
|
||||
| Count build ->
|
||||
Coll.to_seq c
|
||||
|> Map.count_of_seq ~build:(Map.make ~build ())
|
||||
|> PMap.count_of_seq ~build:(PMap.make ~build ())
|
||||
| Lazy -> Lazy.force c
|
||||
|
||||
let _do_binary : type a b c. (a, b, c) binary -> a -> b -> c
|
||||
= fun b c1 c2 -> match b with
|
||||
|
|
@ -565,7 +613,7 @@ let run_no_opt q = _run ~opt:false q
|
|||
|
||||
(** {6 Basics on Collections} *)
|
||||
|
||||
let map f q = Unary (Map f, q)
|
||||
let map f q = Unary (PMap f, q)
|
||||
|
||||
let filter p q = Unary (Filter p, q)
|
||||
|
||||
|
|
@ -581,10 +629,14 @@ let flat_map_seq f q =
|
|||
let f' x = Coll.of_seq (f x) in
|
||||
Unary (FlatMap f', q)
|
||||
|
||||
let flat_map_list f q =
|
||||
let flat_map_l f q =
|
||||
let f' x = Coll.of_list (f x) in
|
||||
Unary (FlatMap f', q)
|
||||
|
||||
let flatten q = Unary (FlatMap (fun x->x), q)
|
||||
|
||||
let flatten_l q = Unary (FlatMap Coll.of_list, q)
|
||||
|
||||
let take n q = Unary (Take n, q)
|
||||
|
||||
let take_while p q = Unary (TakeWhile p, q)
|
||||
|
|
@ -594,26 +646,6 @@ let sort ?(cmp=Pervasives.compare) () q = Unary (Sort cmp, q)
|
|||
let distinct ?(cmp=Pervasives.compare) () q =
|
||||
Unary (Distinct cmp, q)
|
||||
|
||||
let get key q =
|
||||
Unary (Get (Safe, key), q)
|
||||
|
||||
let get_exn key q =
|
||||
Unary (Get (Unsafe, key), q)
|
||||
|
||||
let map_iter q =
|
||||
Unary (GeneralMap (fun m -> Coll.of_seq m.Map.to_seq), q)
|
||||
|
||||
let map_iter_flatten q =
|
||||
let f m = m.Map.to_seq
|
||||
|> CCSequence.flatMap
|
||||
(fun (k,v) -> Coll.to_seq v |> CCSequence.map (fun v' -> k,v'))
|
||||
|> Coll.of_seq
|
||||
in
|
||||
Unary (GeneralMap f, q)
|
||||
|
||||
let map_to_list q =
|
||||
Unary (GeneralMap Map.to_list, q)
|
||||
|
||||
(* choose a build method from the optional arguments *)
|
||||
let _make_build ?cmp ?eq ?hash () =
|
||||
let _maybe default o = match o with
|
||||
|
|
@ -623,23 +655,75 @@ let _make_build ?cmp ?eq ?hash () =
|
|||
match eq, hash with
|
||||
| Some _, _
|
||||
| _, Some _ ->
|
||||
Map.FromHash ( _maybe (=) eq, _maybe Hashtbl.hash hash)
|
||||
PMap.FromHash ( _maybe (=) eq, _maybe Hashtbl.hash hash)
|
||||
| _ ->
|
||||
match cmp with
|
||||
| Some f -> Map.FromCmp f
|
||||
| _ -> Map.Default
|
||||
| Some f -> PMap.FromCmp f
|
||||
| _ -> PMap.Default
|
||||
|
||||
(** {6 Queries on PMaps} *)
|
||||
|
||||
module M = struct
|
||||
let get key q =
|
||||
Unary (Get (Safe, key), q)
|
||||
|
||||
let get_exn key q =
|
||||
Unary (Get (Unsafe, key), q)
|
||||
|
||||
let iter q =
|
||||
Unary (GeneralMap (fun m -> Coll.of_seq m.PMap.to_seq), q)
|
||||
|
||||
let flatten q =
|
||||
let f m = m.PMap.to_seq
|
||||
|> CCSequence.flatMap
|
||||
(fun (k,v) -> Coll.to_seq v |> CCSequence.map (fun v' -> k,v'))
|
||||
|> Coll.of_seq
|
||||
in
|
||||
Unary (GeneralMap f, q)
|
||||
|
||||
let flatten' q =
|
||||
let f m = m.PMap.to_seq
|
||||
|> CCSequence.flatMap
|
||||
(fun (k,v) -> CCSequence.of_list v |> CCSequence.map (fun v' -> k,v'))
|
||||
|> Coll.of_seq
|
||||
in
|
||||
Unary (GeneralMap f, q)
|
||||
|
||||
let map f q =
|
||||
Unary (GeneralMap (PMap.map f), q)
|
||||
|
||||
let to_list q =
|
||||
Unary (GeneralMap PMap.to_list, q)
|
||||
|
||||
let reverse ?cmp ?eq ?hash () q =
|
||||
let build = _make_build ?cmp ?eq ?hash () in
|
||||
Unary (GeneralMap (PMap.reverse ~build), q)
|
||||
|
||||
let reverse_multimap ?cmp ?eq ?hash () q =
|
||||
let build = _make_build ?cmp ?eq ?hash () in
|
||||
Unary (GeneralMap (PMap.reverse_multimap ~build), q)
|
||||
|
||||
let fold f acc q =
|
||||
Unary (FoldMap (f, acc), q)
|
||||
|
||||
let fold_multimap f acc q =
|
||||
let f' acc x l =
|
||||
List.fold_left (fun acc y -> f acc x y) acc l
|
||||
in
|
||||
Unary (FoldMap (f', acc), q)
|
||||
end
|
||||
|
||||
let group_by ?cmp ?eq ?hash f q =
|
||||
Unary (GroupBy (_make_build ?cmp ?eq ?hash (),f), q)
|
||||
|
||||
let group_by' ?cmp ?eq ?hash f q =
|
||||
map_iter (group_by ?cmp f q)
|
||||
M.iter (group_by ?cmp f q)
|
||||
|
||||
let count ?cmp ?eq ?hash () q =
|
||||
Unary (Count (_make_build ?cmp ?eq ?hash ()), q)
|
||||
|
||||
let count' ?cmp () q =
|
||||
map_iter (count ?cmp () q)
|
||||
M.iter (count ?cmp () q)
|
||||
|
||||
let fold f acc q =
|
||||
Unary (Fold (f, acc), q)
|
||||
|
|
@ -764,6 +848,10 @@ let (>>=) x f = Bind (f, x)
|
|||
|
||||
let query_map f q = QueryMap (f, q)
|
||||
|
||||
(** {6 Misc} *)
|
||||
|
||||
let lazy_ q = Unary (Lazy, q)
|
||||
|
||||
(** {6 Output containers} *)
|
||||
|
||||
let to_list q =
|
||||
|
|
@ -787,3 +875,88 @@ let to_stack q =
|
|||
(** {6 Misc} *)
|
||||
|
||||
let run_list q = run (q |> to_list)
|
||||
|
||||
(** {6 Adapters} *)
|
||||
|
||||
module AdaptSet(S : Set.S) = struct
|
||||
let of_set set =
|
||||
return (Coll.of_seq (fun k -> S.iter k set))
|
||||
|
||||
let to_set q =
|
||||
let f c =
|
||||
Coll.to_seq c |> CCSequence.fold (fun set x -> S.add x set) S.empty
|
||||
in
|
||||
query_map f q
|
||||
|
||||
let run q = run (q |> to_set)
|
||||
end
|
||||
|
||||
module AdaptMap(M : Map.S) = struct
|
||||
let _to_seq m k = M.iter (fun x y -> k (x,y)) m
|
||||
|
||||
let of_map map =
|
||||
return (Coll.of_seq (_to_seq map))
|
||||
|
||||
let to_pmap m = {
|
||||
PMap.get = (fun x -> try Some (M.find x m) with Not_found -> None);
|
||||
PMap.size = (fun () -> M.cardinal m);
|
||||
PMap.is_empty = (fun () -> M.is_empty m);
|
||||
PMap.fold = (fun f acc -> M.fold (fun x y acc -> f acc x y) m acc);
|
||||
PMap.to_seq = _to_seq m;
|
||||
}
|
||||
|
||||
let to_map q =
|
||||
let f c =
|
||||
Coll.to_seq c
|
||||
|> CCSequence.fold (fun m (x,y) -> M.add x y m) M.empty
|
||||
in
|
||||
query_map f q
|
||||
|
||||
let run q = run (q |> to_map)
|
||||
end
|
||||
|
||||
module IO = struct
|
||||
let slurp ic =
|
||||
let l = lazy (
|
||||
let buf_size = 256 in
|
||||
let content = Buffer.create 120
|
||||
and buf = String.make buf_size 'a' in
|
||||
let rec next () =
|
||||
let num = input ic buf 0 buf_size in
|
||||
if num = 0
|
||||
then Buffer.contents content (* EOF *)
|
||||
else (Buffer.add_substring content buf 0 num; next ())
|
||||
in next ()
|
||||
) in
|
||||
lazy_ (return l)
|
||||
|
||||
(* find [c] in [s], starting at offset [i] *)
|
||||
let rec _find s c i =
|
||||
if i >= String.length s then None
|
||||
else if s.[i] = c then Some i
|
||||
else _find s c (i+1)
|
||||
|
||||
let rec _lines s i k = match _find s '\n' i with
|
||||
| None -> ()
|
||||
| Some j ->
|
||||
let s' = String.sub s i (j-i) in
|
||||
k s';
|
||||
_lines s (j+1) k
|
||||
|
||||
let lines q =
|
||||
(* sequence of lines *)
|
||||
let f s = _lines s 0 |> Coll.of_seq in
|
||||
query_map f q
|
||||
|
||||
let lines' q =
|
||||
let f s = lazy (_lines s 0 |> CCSequence.to_list) in
|
||||
lazy_ (query_map f q)
|
||||
|
||||
let out oc q =
|
||||
run q |> output_string oc
|
||||
|
||||
let out_lines oc q =
|
||||
run q
|
||||
|> Coll.to_seq
|
||||
|> CCSequence.iter (fun l -> output_string oc l; output_char oc '\n')
|
||||
end
|
||||
|
|
|
|||
122
core/CCLinq.mli
122
core/CCLinq.mli
|
|
@ -38,10 +38,10 @@ the order of execution.
|
|||
|
||||
CCLinq.(
|
||||
start_list [1;2;3]
|
||||
|> flat_map_list (fun x -> CCList.(x -- (x+10)))
|
||||
|> flat_map_l (fun x -> CCList.(x -- (x+10)))
|
||||
|> sort ()
|
||||
|> count ()
|
||||
|> map_to_list |> run
|
||||
|> M.to_list |> run
|
||||
);;
|
||||
- : (int * int) list = [(13, 1); (12, 2); (11, 3); (10, 3); (9, 3);
|
||||
(8, 3); (7, 3); (6, 3); (5, 3); (4, 3); (3, 3); (2, 2); (1, 1)]
|
||||
|
|
@ -59,8 +59,8 @@ type 'a collection
|
|||
be used directly, they are to be processed using a query (type {!'a t})
|
||||
and converted to some list/sequence/array *)
|
||||
|
||||
(** {2 A polymorphic map} *)
|
||||
module Map : sig
|
||||
(** {2 Polymorphic Maps} *)
|
||||
module PMap : sig
|
||||
type ('a, 'b) t
|
||||
|
||||
val get : ('a,'b) t -> 'a -> 'b option
|
||||
|
|
@ -74,6 +74,8 @@ module Map : sig
|
|||
val to_seq : ('a, 'b) t -> ('a * 'b) sequence
|
||||
|
||||
val to_list : ('a, 'b) t -> ('a * 'b) list
|
||||
|
||||
val to_coll : ('a, 'b) t -> ('a * 'b) collection
|
||||
end
|
||||
|
||||
(** {2 Query operators} *)
|
||||
|
|
@ -86,16 +88,24 @@ type 'a t
|
|||
val start : 'a -> 'a t
|
||||
(** Start with a single value *)
|
||||
|
||||
val start_list : 'a list -> 'a collection t
|
||||
val of_list : 'a list -> 'a collection t
|
||||
(** Query that just returns the elements of the list *)
|
||||
|
||||
val start_array : 'a array -> 'a collection t
|
||||
val of_array : 'a array -> 'a collection t
|
||||
val of_array_i : 'a array -> (int * 'a) collection t
|
||||
|
||||
val start_hashtbl : ('a,'b) Hashtbl.t -> ('a * 'b) collection t
|
||||
val of_hashtbl : ('a,'b) Hashtbl.t -> ('a * 'b) collection t
|
||||
|
||||
val start_seq : 'a sequence -> 'a collection t
|
||||
val of_seq : 'a sequence -> 'a collection t
|
||||
(** Query that returns the elements of the given sequence. *)
|
||||
|
||||
val of_queue : 'a Queue.t -> 'a collection t
|
||||
|
||||
val of_stack : 'a Stack.t -> 'a collection t
|
||||
|
||||
val of_string : string -> char collection t
|
||||
(** Traverse the characters of the string *)
|
||||
|
||||
(** {6 Execution} *)
|
||||
|
||||
val run : 'a t -> 'a
|
||||
|
|
@ -132,7 +142,11 @@ val flat_map : ('a -> 'b collection) -> 'a collection t -> 'b collection t
|
|||
val flat_map_seq : ('a -> 'b sequence) -> 'a collection t -> 'b collection t
|
||||
(** Same as {!flat_map} but using sequences *)
|
||||
|
||||
val flat_map_list : ('a -> 'b list) -> 'a collection t -> 'b collection t
|
||||
val flat_map_l : ('a -> 'b list) -> 'a collection t -> 'b collection t
|
||||
|
||||
val flatten : 'a collection collection t -> 'a collection t
|
||||
|
||||
val flatten_l : 'a list collection t -> 'a collection t
|
||||
|
||||
val take : int -> 'a collection t -> 'a collection t
|
||||
(** take at most [n] elements *)
|
||||
|
|
@ -147,27 +161,50 @@ val distinct : ?cmp:'a ord -> unit -> 'a collection t -> 'a collection t
|
|||
(** Remove duplicate elements from the input collection.
|
||||
All elements in the result are distinct. *)
|
||||
|
||||
(** {6 Maps} *)
|
||||
(** {6 Queries on Maps} *)
|
||||
|
||||
val get : 'a -> ('a, 'b) Map.t t -> 'b option t
|
||||
(** Select a key from a map *)
|
||||
module M : sig
|
||||
val get : 'a -> ('a, 'b) PMap.t t -> 'b option t
|
||||
(** Select a key from a map *)
|
||||
|
||||
val get_exn : 'a -> ('a, 'b) Map.t t -> 'b t
|
||||
(** Unsafe version of {!get}.
|
||||
@raise Not_found if the key is not present. *)
|
||||
val get_exn : 'a -> ('a, 'b) PMap.t t -> 'b t
|
||||
(** Unsafe version of {!get}.
|
||||
@raise Not_found if the key is not present. *)
|
||||
|
||||
val map_iter : ('a,'b) Map.t t -> ('a*'b) collection t
|
||||
(** View a multimap as a proper collection *)
|
||||
val iter : ('a,'b) PMap.t t -> ('a*'b) collection t
|
||||
(** View a multimap as a proper collection *)
|
||||
|
||||
val map_iter_flatten : ('a,'b collection) Map.t t -> ('a*'b) collection t
|
||||
(** View a multimap as a collection of individual key/value pairs *)
|
||||
val flatten : ('a,'b collection) PMap.t t -> ('a*'b) collection t
|
||||
(** View a multimap as a collection of individual key/value pairs *)
|
||||
|
||||
val map_to_list : ('a,'b) Map.t t -> ('a*'b) list t
|
||||
val flatten' : ('a,'b list) PMap.t t -> ('a*'b) collection t
|
||||
(** View a multimap as a collection of individual key/value pairs *)
|
||||
|
||||
val map : ('b -> 'c) -> ('a, 'b) PMap.t t -> ('a, 'c) PMap.t t
|
||||
(** Transform values *)
|
||||
|
||||
val to_list : ('a,'b) PMap.t t -> ('a*'b) list t
|
||||
|
||||
val reverse : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash -> unit ->
|
||||
('a,'b) PMap.t t -> ('b,'a list) PMap.t t
|
||||
(** Reverse relation of the map, as a multimap *)
|
||||
|
||||
val reverse_multimap : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash -> unit ->
|
||||
('a,'b list) PMap.t t -> ('b,'a list) PMap.t t
|
||||
(** Reverse relation of the multimap *)
|
||||
|
||||
val fold : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> ('a,'b) PMap.t t -> 'acc t
|
||||
(** Fold on the items of the map *)
|
||||
|
||||
val fold_multimap : ('acc -> 'a -> 'b -> 'acc) -> 'acc ->
|
||||
('a,'b list) PMap.t t -> 'acc t
|
||||
(** Fold on the items of the multimap *)
|
||||
end
|
||||
|
||||
(** {6 Aggregation} *)
|
||||
|
||||
val group_by : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash ->
|
||||
('a -> 'b) -> 'a collection t -> ('b,'a list) Map.t t
|
||||
('a -> 'b) -> 'a collection t -> ('b,'a list) PMap.t t
|
||||
(** [group_by f] takes a collection [c] as input, and returns
|
||||
a multimap [m] such that for each [x] in [c],
|
||||
[x] occurs in [m] under the key [f x]. In other words, [f] is used
|
||||
|
|
@ -177,7 +214,7 @@ val group_by' : ?cmp:'b ord -> ?eq:'b equal -> ?hash:'b hash ->
|
|||
('a -> 'b) -> 'a collection t -> ('b * 'a list) collection t
|
||||
|
||||
val count : ?cmp:'a ord -> ?eq:'a equal -> ?hash:'a hash ->
|
||||
unit -> 'a collection t -> ('a, int) Map.t t
|
||||
unit -> 'a collection t -> ('a, int) PMap.t t
|
||||
(** [count c] returns a map from elements of [c] to the number
|
||||
of time those elements occur. *)
|
||||
|
||||
|
|
@ -189,7 +226,8 @@ val fold : ('b -> 'a -> 'b) -> 'b -> 'a collection t -> 'b t
|
|||
val size : _ collection t -> int t
|
||||
(** Count how many elements the collection contains *)
|
||||
|
||||
val reduce : ('a -> 'b) -> ('a -> 'b -> 'b) -> ('b -> 'c) -> 'a collection t -> 'c option t
|
||||
val reduce : ('a -> 'b) -> ('a -> 'b -> 'b) -> ('b -> 'c) ->
|
||||
'a collection t -> 'c option t
|
||||
(** [reduce start mix stop q] uses [start] on the first element of [q],
|
||||
and combine the result with following elements using [mix]. The final
|
||||
value is transformed using [stop]. This returns [None] if the collection
|
||||
|
|
@ -234,7 +272,7 @@ val join : ?cmp:'key ord -> ?eq:'key equal -> ?hash:'key hash ->
|
|||
|
||||
val group_join : ?cmp:'a ord -> ?eq:'a equal -> ?hash:'a hash ->
|
||||
('b -> 'a) -> 'a collection t -> 'b collection t ->
|
||||
('a, 'b list) Map.t t
|
||||
('a, 'b list) PMap.t t
|
||||
(** [group_join key2] associates to every element [x] of
|
||||
the first collection, all the elements [y] of the second
|
||||
collection such that [eq x (key y)] *)
|
||||
|
|
@ -293,7 +331,11 @@ val return : 'a -> 'a t
|
|||
(** Synonym to {!start} *)
|
||||
|
||||
val query_map : ('a -> 'b) -> 'a t -> 'b t
|
||||
(** Map results directly, rather than collections of elements *)
|
||||
(** PMap results directly, rather than collections of elements *)
|
||||
|
||||
(** {6 Misc} *)
|
||||
|
||||
val lazy_ : 'a lazy_t t -> 'a t
|
||||
|
||||
(** {6 Output Containers} *)
|
||||
|
||||
|
|
@ -313,3 +355,33 @@ val to_hashtbl : ('a * 'b) collection t -> ('a, 'b) Hashtbl.t t
|
|||
val to_queue : 'a collection t -> ('a Queue.t -> unit) t
|
||||
|
||||
val to_stack : 'a collection t -> ('a Stack.t -> unit) t
|
||||
|
||||
(** {6 Adapters} *)
|
||||
|
||||
module AdaptSet(S : Set.S) : sig
|
||||
val of_set : S.t -> S.elt collection t
|
||||
val to_set : S.elt collection t -> S.t t
|
||||
val run : S.elt collection t -> S.t
|
||||
end
|
||||
|
||||
module AdaptMap(M : Map.S) : sig
|
||||
val of_map : 'a M.t -> (M.key * 'a) collection t
|
||||
val to_pmap : 'a M.t -> (M.key, 'a) PMap.t
|
||||
val to_map : (M.key * 'a) collection t -> 'a M.t t
|
||||
val run : (M.key * 'a) collection t -> 'a M.t
|
||||
end
|
||||
|
||||
module IO : sig
|
||||
val slurp : in_channel -> string t
|
||||
(** Slurp the whole channel in (blocking), returning the corresponding string *)
|
||||
|
||||
val lines : string t -> string collection t
|
||||
(** Convert a string into a collection of lines *)
|
||||
|
||||
val lines' : string t -> string list t
|
||||
(** Convert a string into a list of lines *)
|
||||
|
||||
val out : out_channel -> string t -> unit
|
||||
val out_lines : out_channel -> string collection t -> unit
|
||||
(** Evaluate the query and print it line by line on the output *)
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue