From 9b3df4570a2e747fe0b4384a3313ee73f0dafd86 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 25 Dec 2016 19:36:23 +0100 Subject: [PATCH 1/9] remove deprecated functions, add missing links to `SequenceLabels` --- src/sequence.ml | 10 ++-------- src/sequence.mli | 14 +++----------- src/sequenceLabels.mli | 27 ++++++++++++++++++++------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/sequence.ml b/src/sequence.ml index 96d2452..b5ce58d 100644 --- a/src/sequence.ml +++ b/src/sequence.ml @@ -133,9 +133,7 @@ let concat s k = s (fun s' -> s' k) let flatten s = concat s -let flatMap f seq k = seq (fun x -> f x k) - -let flat_map = flatMap +let flat_map f seq k = seq (fun x -> f x k) (*$R (1 -- 1000) @@ -147,14 +145,12 @@ let flat_map = flatMap let flat_map_l f seq k = seq (fun x -> List.iter k (f x)) -let fmap f seq k = +let filter_map f seq k = seq (fun x -> match f x with | None -> () | Some y -> k y ) -let filter_map = fmap - let intersperse elem seq k = let first = ref true in seq (fun x -> (if !first then first := false else k elem); k x) @@ -337,8 +333,6 @@ let group_succ_by ?(eq=fun x y -> x = y) seq k = (* last list *) if !cur <> [] then k !cur -let group = group_succ_by - (*$R [1;2;3;3;2;2;3;4] |> of_list |> group_succ_by ?eq:None |> to_list diff --git a/src/sequence.mli b/src/sequence.mli index 9a501ce..379a158 100644 --- a/src/sequence.mli +++ b/src/sequence.mli @@ -150,23 +150,19 @@ val concat : 'a t t -> 'a t val flatten : 'a t t -> 'a t (** Alias for {!concat} *) -val flatMap : ('a -> 'b t) -> 'a t -> 'b t -(** @deprecated use {!flat_map} since 0.6 *) - val flat_map : ('a -> 'b t) -> 'a t -> 'b t (** Monadic bind. Intuitively, it applies the function to every element of the initial sequence, and calls {!concat}. + Formerly [flatMap] @since 0.5 *) val flat_map_l : ('a -> 'b list) -> 'a t -> 'b t (** Convenience function combining {!flat_map} and {!of_list} @since NEXT_RELEASE *) -val fmap : ('a -> 'b option) -> 'a t -> 'b t -(** @deprecated use {!filter_map} since 0.6 *) - val filter_map : ('a -> 'b option) -> 'a t -> 'b t (** Map and only keep non-[None] elements + Formerly [fmap] @since 0.5 *) val intersperse : 'a -> 'a t -> 'a t @@ -201,13 +197,9 @@ val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t (** Sort the sequence and remove duplicates. Eager, same as [sort] *) -val group : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t -(** Group equal consecutive elements. - @deprecated since 0.6 use {!group_succ_by} *) - val group_succ_by : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t (** Group equal consecutive elements. - Synonym to {!group}. + Formerly synonym to [group]. @since 0.6 *) val group_by : ?hash:('a -> int) -> ?eq:('a -> 'a -> bool) -> diff --git a/src/sequenceLabels.mli b/src/sequenceLabels.mli index 5db0707..1a092eb 100644 --- a/src/sequenceLabels.mli +++ b/src/sequenceLabels.mli @@ -126,14 +126,12 @@ val concat : 'a t t -> 'a t val flatten : 'a t t -> 'a t (** Alias for {!concat} *) -val flatMap : f:('a -> 'b t) -> 'a t -> 'b t -(** @deprecated use {!flat_map} *) - val flat_map : f:('a -> 'b t) -> 'a t -> 'b t (** Alias to {!flatMap} with a more explicit name *) -val fmap : f:('a -> 'b option) -> 'a t -> 'b t -(** @deprecated use {!filter_map} *) +val flat_map_l : f:('a -> 'b list) -> 'a t -> 'b t +(** Convenience function combining {!flat_map} and {!of_list} + @since NEXT_RELEASE *) val filter_map : f:('a -> 'b option) -> 'a t -> 'b t (** Alias to {!fmap} with a more explicit name *) @@ -170,8 +168,16 @@ val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t (** Sort the sequence and remove duplicates. Eager, same as [sort] *) -val group : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t -(** Group equal consecutive elements. *) +val group_succ_by : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t +(** Group equal consecutive elements. + Formerly synonym to [group]. + @since 0.6 *) + +val group_by : ?hash:('a -> int) -> ?eq:('a -> 'a -> bool) -> + 'a t -> 'a list t +(** Group equal elements, disregarding their order of appearance. + The result sequence is traversable as many times as required. + @since 0.6 *) val uniq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t (** Remove consecutive duplicate elements. Basically this is @@ -369,6 +375,13 @@ val int_range_dec : start:int -> stop:int -> int t (** Iterator on decreasing integers in [stop...start] by steps -1. See {!(--^)} for an infix version *) +val int_range_by : step:int -> start:int -> stop:int -> int t +(** [int_range_by ~step ~start:i ~stop:j] is the range starting at [i], including [j], + where the difference between successive elements is [step]. + use a negative [step] for a decreasing sequence. + @since NEXT_RELEASE + @raise Invalid_argument if [step=0] *) + val of_set : (module Set.S with type elt = 'a and type t = 'b) -> 'b -> 'a t (** Convert the given set to a sequence. The set module must be provided. *) From e6af6792d28d45d4446d3388ff8194bc78356045 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 25 Dec 2016 20:15:11 +0100 Subject: [PATCH 2/9] update merlin file --- .merlin | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.merlin b/.merlin index d904327..a047e25 100644 --- a/.merlin +++ b/.merlin @@ -1,9 +1,10 @@ -S . +S src S bench/ S tests/ -B _build +B _build/src B _build/tests/ B _build/bench/ -PKG oUnit +PKG oUnit qcheck PKG benchmark -FLAG -safe-string +FLG -safe-string +FLG -w +a -w -4 -w -44 From 4c6cd1560c91299df4a8a194edf5c148cd94523a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 25 Dec 2016 20:15:17 +0100 Subject: [PATCH 3/9] update benchmarks --- bench/benchs.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bench/benchs.ml b/bench/benchs.ml index af8b5db..2fa414d 100644 --- a/bench/benchs.ml +++ b/bench/benchs.ml @@ -10,10 +10,10 @@ let bench_fold n = 0 -- n |> S.fold (+) 0 |> ignore let bench_flatmap n = - 0 -- n |> S.flatMap (fun i -> i -- (i+5)) |> (fun _ -> ()) + 0 -- n |> S.flat_map (fun i -> i -- (i+5)) |> (fun _ -> ()) let bench_product n = - S.product (0 -- n) (0 -- n) (fun (i,j) -> ()) + S.product (0 -- n) (0 -- n) (fun _ -> ()) let _ = List.iter From e2bd8fa4f7f66a1863c18249f968c80c8ac1b931 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 25 Dec 2016 21:32:28 +0100 Subject: [PATCH 4/9] update readme: convert into asciidoc, add tutorial --- README.adoc | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 57 --------------- 2 files changed, 198 insertions(+), 57 deletions(-) create mode 100644 README.adoc delete mode 100644 README.md diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..bcabf40 --- /dev/null +++ b/README.adoc @@ -0,0 +1,198 @@ += Sequence +:toc: macro +:source-highlighter: pygments + +Simple sequence abstract datatype, intended to iterate efficiently +on collections while performing some transformations. + +Common operations supported by Sequence include ` +`filter`, `map`, `take`, `drop`, `append`, `flat_map`, etc. +Sequence is not designed to be as general-purpose or flexible as, say, +Batteries' `'a Enum.t`. Rather, it aims at providing a very simple and efficient +way of iterating on a finite number of values, only allocating (most of the time) +one intermediate closure to do so. For instance, iterating on keys, or values, +of a `Hashtbl.t`, without creating a list. + +== Documentation + +There is only one important type, `'a Sequence.t`, and lots of functions built +around this type. +To get an overview of sequence, its origins and why it was created, +you can start with http://cedeela.fr/~simon/talks/sequence.pdf[the slides of a talk] +I (@c-cube) made at some OCaml meeting. + +See https://c-cube.github.io/sequence/api/[the online API] +for more details on the set of available functions. + +== Build + +1. via opam `opam install sequence` +2. manually (need OCaml >= 3.12): `make all install` + +If you have https://github.com/vincent-hugot/iTeML[qtest] installed, +you can build and run tests with + +---- +$ ./configure --enable-tests +$ make test +---- + +If you have https://github.com/Chris00/ocaml-benchmark[benchmarks] installed, +you can build and run benchmarks with + +---- +$ make benchs +$ ./benchs.native +---- + +To see how to use the library, check the `examples` directory. +`tests.ml` has a few examples of how to convert basic data structures into +sequences, and conversely. + +== Short Tutorial + +=== Transferring Data + +TODO: transfer queue->stack, hashtbl->list +Conversion between n container types +would take n² functions. In practice, for a given collection +we can at best hope for `to_list` and `of_list`. +With sequence, if the source structure provides a +`iter` function (or a `to_seq` wrapper), it becomes: + +[source,OCaml] +---- +# let q = Queue.create();; +# Sequence.( 1 -- 10 |> to_queue q);; +- : unit = () +# Sequence.of_queue q |> Sequence.to_list ;; +- : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] + +# let s = Stack.create();; +# Sequence.(of_queue q |> to_stack s);; +- : unit = () +# Sequence.of_stack s |> Sequence.to_list ;; +- : int list = [10; 9; 8; 7; 6; 5; 4; 3; 2; 1] +---- + +Note how the list of elements is reversed when we transfer them +from the queue to the stack. + +Another example is extracting the list of values of +a hashtable (in an undefined order that depends on the +underlying hash function): + +[source,OCaml] +---- +# let h = Hashtbl.create 16;; +# for i = 0 to 10 do + Hashtbl.add h i (string_of_int i) + done;; +- : unit = () + +# Hashtbl.length h;; +- : int = 11 + +(* now to get the values *) +# Sequence.of_t +# Sequence.of_hashtbl h |> Sequence.map snd |> Sequence.to_list;; +- : string list = ["6"; "2"; "8"; "7"; "3"; "5"; "4"; "9"; "0"; "10"; "1"] +---- + +=== Replacing `for` loops + +The `for` loop is a bit limited, and lacks compositionality. +Instead, it can be more convenient and readable to +use `Sequence.(--) : int -> int -> int Sequence.t`. + +[source,OCaml] +---- +# Sequence.(1 -- 10_000_000 |> fold (+) 0);; +- : int = 50000005000000 + +# let p x = x mod 5 = 0 in + Sequence.(1 -- 5_000 + |> filter p + |> map (fun x -> x * x) + |> fold (+) 0 + );; +- : int = 8345837500 +---- + +NOTE: with **flambda** under sufficiently strong +optimization flags, such compositions of operators +will be compiled to an actual loop with no overhead! + +=== Iterating on sub-trees + +A small λ-calculus AST, and some operations on it. + +[source,OCaml] +---- +# type term = + | Var of string + | App of term * term + | Lambda of term ;; + +# let rec subterms : term -> term Sequence.t = + fun t -> + let open Sequence.Infix in + Sequence.cons t + (match t with + | Var _ -> Sequence.empty + | Lambda u -> subterms u + | App (a,b) -> + Sequence.append (subterms a) (subterms b)) + ;; + +(* Now we can define many other functions easily! *) +# let vars t = + Sequence.filter_map + (function Var s -> Some s | _ -> None) + (subterms t) ;; +val vars : term -> string sequence = + +# let size t = Sequence.length (subterms t) ;; +val size : term -> int = + +# let vars_list l = Sequence.(of_list l |> flat_map vars);; +val vars_list : term list -> string sequence = +---- + +=== Permutations + +Makes it easy to write backtracking code (a non-deterministic +function returning several '`a' +will just return a `'a Sequence.t`). +Here, we generate all permutations of a list by +enumerating the ways we can insert an element in a list. + +[source,OCaml] +---- +# module S = Sequence ;; +# let rec insert x l = match l with + | [] -> S.return [x] + | y :: tl -> + S.append + S.(insert x tl >|= fun tl' -> y :: tl') + (S.return (x :: l)) ;; + +# let rec permute l = match l with + | [] -> S.return [] + | x :: tl -> permute tl >>= insert x ;; + +# permute [1;2;3;4] |> S.take 2 |> S.to_list ;; +- : int list list = [[4; 3; 2; 1]; [4; 3; 1; 2]] + +---- + +=== Advanced example + +The module `examples/sexpr.mli` exposes the interface of the S-expression +example library. It requires OCaml>=4.0 to compile, because of the GADT +structure used in the monadic parser combinators part of `examples/sexpr.ml`. +Be careful that this is quite obscure. + +== License + +Sequence is available under the BSD license. diff --git a/README.md b/README.md deleted file mode 100644 index adb639c..0000000 --- a/README.md +++ /dev/null @@ -1,57 +0,0 @@ -Sequence -======== - -Simple sequence abstract datatype, intended to transfer a finite number of -elements from one data structure to another. Some transformations on sequences, -like `filter`, `map`, `take`, `drop` and `append` can be performed before the -sequence is iterated/folded on. - -Sequence is not designed to be as general-purpose or flexible as, say, -Batteries' `Enum.t`. Rather, it aims at providing a very simple and efficient -way of iterating on a finite number of values, only allocating (most of the time) -one intermediate closure to do so. For instance, iterating on keys, or values, -of a `Hashtbl.t`, without creating a list. - -Documentation -============= - -There is only one type, `'a Sequence.t`, and lots of functions built around -this type. -To get an overview of sequence, its origins and why it was created, -you can start with [the slides of a talk](http://cedeela.fr/~simon/talks/sequence.pdf) -I (c-cube) made at some OCaml meeting. - -See [the online API](http://cedeela.fr/~simon/software/sequence/Sequence.html) -for more details on the set of available functions. - -Build -===== - -1. via opam `opam install sequence` -2. manually (need OCaml >= 3.12): `make all install` - -If you have `OUnit` installed, you can build and run tests with - - $ make tests - $ ./run_tests.native - -If you have `Bench` installed, you can build and run benchmarks with - - $ make benchs - $ ./benchs.native - -To see how to use the library, check the `examples` directory. -`tests.ml` has a few examples of how to convert basic data structures into -sequences, and conversely. - -Examples -======== - -The module `examples/sexpr.mli` exposes the interface of the S-expression -example library. It requires OCaml>=4.0 to compile, because of the GADT -structure used in the monadic parser combinators part of `examples/sexpr.ml`. - -License -======= - -Sequence is available under the BSD license. From e71e9560ccab42e56f9ce647f491e91692101e1b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 25 Dec 2016 21:35:48 +0100 Subject: [PATCH 5/9] fix a few typos --- README.adoc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index bcabf40..ab9fc56 100644 --- a/README.adoc +++ b/README.adoc @@ -5,7 +5,7 @@ Simple sequence abstract datatype, intended to iterate efficiently on collections while performing some transformations. -Common operations supported by Sequence include ` +Common operations supported by Sequence include `filter`, `map`, `take`, `drop`, `append`, `flat_map`, etc. Sequence is not designed to be as general-purpose or flexible as, say, Batteries' `'a Enum.t`. Rather, it aims at providing a very simple and efficient @@ -53,7 +53,6 @@ sequences, and conversely. === Transferring Data -TODO: transfer queue->stack, hashtbl->list Conversion between n container types would take n² functions. In practice, for a given collection we can at best hope for `to_list` and `of_list`. @@ -162,7 +161,7 @@ val vars_list : term list -> string sequence = === Permutations Makes it easy to write backtracking code (a non-deterministic -function returning several '`a' +function returning several `'a` will just return a `'a Sequence.t`). Here, we generate all permutations of a list by enumerating the ways we can insert an element in a list. From b062ccfd70d17f52d21bd59e0d0a55029f9df440 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 26 Dec 2016 00:44:57 +0100 Subject: [PATCH 6/9] add `make watch` --- Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9908566..69c58f4 100644 --- a/Makefile +++ b/Makefile @@ -80,4 +80,10 @@ update_next_tag: sed -i "s/NEXT_VERSION/$(VERSION)/g" *.ml *.mli sed -i "s/NEXT_RELEASE/$(VERSION)/g" *.ml *.mli -.PHONY: benchs tests examples update_next_tag push_doc push_stable +watch: + while find src/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ + echo "============ at `date` ==========" ; \ + make all; \ + done + +.PHONY: benchs tests examples update_next_tag push_doc push_stable watch From 4748655708de373c610d3f8c113690f0dbbcf808 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 26 Dec 2016 00:45:03 +0100 Subject: [PATCH 7/9] =?UTF-8?q?add=20`init,fold=5Fmap,fold=5Ffilter=5Fmap,?= =?UTF-8?q?sorted,diagonal,findi,=E2=80=A6`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sequence.ml | 79 ++++++++++++++++++++++++++++++++++++++++++ src/sequence.mli | 38 ++++++++++++++++++++ src/sequenceLabels.mli | 42 ++++++++++++++++++++++ 3 files changed, 159 insertions(+) diff --git a/src/sequence.ml b/src/sequence.ml index b5ce58d..1e38e3b 100644 --- a/src/sequence.ml +++ b/src/sequence.ml @@ -48,6 +48,17 @@ let repeat x k = while true do k x done (seq |> take 3 |> to_list); *) +let init f yield = + let rec aux i = + yield (f i); + aux (i+1) + in + aux 0 + +(*$= + [0;1;2;3;4] (init (fun x->x) |> take 5 |> to_list) +*) + let rec iterate f x k = k x; iterate f (f x) k @@ -94,6 +105,28 @@ let foldi f init seq = OUnit.assert_equal [1, "world"; 0, "hello"] l; *) +let fold_map f init seq yield = + let r = ref init in + seq + (fun x -> + let acc', y = f !r x in + r := acc'; + yield y) + +(*$= & ~printer:Q.Print.(list int) + [0;1;3;5] (0--3 |> fold_map (fun prev x -> x,prev+x) 0 |> to_list) +*) + +let fold_filter_map f init seq yield = + let r = ref init in + seq + (fun x -> + let acc', y = f !r x in + r := acc'; + match y with + | None -> () + | Some y' -> yield y') + let map f seq k = seq (fun x -> k (f x)) let mapi f seq k = @@ -320,6 +353,23 @@ let sort ?(cmp=Pervasives.compare) seq = |> OUnit.assert_equal [100;99;98;97] *) +exception Exit_sorted + +let sorted ?(cmp=Pervasives.compare) seq = + let prev = ref None in + try + seq (fun x -> match !prev with + | Some y when cmp y x > 0 -> raise Exit_sorted + | _ -> prev := Some x); + true + with Exit_sorted -> false + +(*$T + of_list [1;2;3;4] |> sorted + not (of_list [1;2;3;0;4] |> sorted) + sorted empty +*) + let group_succ_by ?(eq=fun x y -> x = y) seq k = let cur = ref [] in seq (fun x -> @@ -413,6 +463,16 @@ let product outer inner k = let product2 outer inner k = outer (fun x -> inner (fun y -> k x y)) +let rec diagonal l yield = match l with + | [] -> () + | x::tail -> + List.iter (fun y -> yield (x,y)) tail; + diagonal tail yield + +(*$= + [0,1; 0,2; 1,2] (diagonal [0;1;2] |> to_list) + *) + let join ~join_row s1 s2 k = s1 (fun a -> s2 (fun b -> @@ -607,6 +667,25 @@ let find f seq = end; !r +let findi f seq = + let i = ref 0 in + let r = ref None in + begin + try + seq + (fun x -> match f !i x with + | None -> incr i + | Some _ as res -> r := res; raise ExitFind); + with ExitFind -> () + end; + !r + +let find_pred f seq = find (fun x -> if f x then Some x else None) seq + +let find_pred_exn f seq = match find_pred f seq with + | Some x -> x + | None -> raise Not_found + let length seq = let r = ref 0 in seq (fun _ -> incr r); diff --git a/src/sequence.mli b/src/sequence.mli index 379a158..bfc8ac1 100644 --- a/src/sequence.mli +++ b/src/sequence.mli @@ -57,6 +57,10 @@ val singleton : 'a -> 'a t val doubleton : 'a -> 'a -> 'a t (** Sequence with exactly two elements *) +val init : (int -> 'a) -> 'a t +(** [init f] is the infinite sequence [f 0; f 1; f 2; …]. + @since NEXT_RELEASE *) + val cons : 'a -> 'a t -> 'a t (** [cons x l] yields [x], then yields from [l]. Same as [append (singleton x) l] *) @@ -103,6 +107,17 @@ val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a (** Fold over elements of the sequence and their index, consuming it *) +val fold_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a t -> 'b t +(** [fold_map f acc l] is like {!map}, but it carries some state as in + {!fold}. The state is not returned, it is just used to thread some + information to the map function. + @since NEXT_RELEASE *) + +val fold_filter_map : ('acc -> 'a -> 'acc * 'b option) -> 'acc -> 'a t -> 'b t +(** [fold_filter_map f acc l] is a {!fold_map}-like function, but the + function can choose to skip an element by retuning [None]. + @since NEXT_RELEASE *) + val map : ('a -> 'b) -> 'a t -> 'b t (** Map objects of the sequence into other elements, lazily *) @@ -129,6 +144,20 @@ val find : ('a -> 'b option) -> 'a t -> 'b option (** Find the first element on which the function doesn't return [None] @since 0.5 *) +val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option +(** Indexed version of {!find} + @since NEXT_RELEASE *) + +val find_pred : ('a -> bool) -> 'a t -> 'a option +(** [find_pred p l] finds the first element of [l] that satisfies [p], + or returns [None] if no element satisfies [p] + @since NEXT_RELEASE *) + +val find_pred_exn : ('a -> bool) -> 'a t -> 'a +(** Unsafe version of {!find_pred} + @raise Not_found if no such element is found + @since NEXT_RELEASE *) + val length : 'a t -> int (** How long is the sequence? Forces the sequence. *) @@ -197,6 +226,10 @@ val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t (** Sort the sequence and remove duplicates. Eager, same as [sort] *) +val sorted : ?cmp:('a -> 'a -> int) -> 'a t -> bool +(** Checks whether the sequence is sorted. Eager, same as {!sort}. + @since NEXT_RELEASE *) + val group_succ_by : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t (** Group equal consecutive elements. Formerly synonym to [group]. @@ -218,6 +251,11 @@ val product : 'a t -> 'b t -> ('a * 'b) t as required (several times), possibly by calling {!persistent} on it beforehand. *) +val diagonal : 'a list -> ('a * 'a) t +(** All pairs of distinct positions of the list. [diagonal l] will + return the list of [List.nth i l, List.nth j l] if [i < j]. + @since NEXT_RELEASE *) + val product2 : 'a t -> 'b t -> ('a, 'b) t2 (** Binary version of {!product}. Same requirements. @since 0.5 *) diff --git a/src/sequenceLabels.mli b/src/sequenceLabels.mli index 1a092eb..6ef97f3 100644 --- a/src/sequenceLabels.mli +++ b/src/sequenceLabels.mli @@ -35,6 +35,10 @@ val singleton : 'a -> 'a t val doubleton : 'a -> 'a -> 'a t (** Sequence with exactly two elements *) +val init : f:(int -> 'a) -> 'a t +(** [init f] is the infinite sequence [f 0; f 1; f 2; …]. + @since NEXT_RELEASE *) + val cons : 'a -> 'a t -> 'a t (** [cons x l] yields [x], then yields from [l]. Same as [append (singleton x) l] *) @@ -81,6 +85,17 @@ val fold : f:('a -> 'b -> 'a) -> init:'a -> 'b t -> 'a val foldi : f:('a -> int -> 'b -> 'a) -> init:'a -> 'b t -> 'a (** Fold over elements of the sequence and their index, consuming it *) +val fold_map : f:('acc -> 'a -> 'acc * 'b) -> init:'acc -> 'a t -> 'b t +(** [fold_map f acc l] is like {!map}, but it carries some state as in + {!fold}. The state is not returned, it is just used to thread some + information to the map function. + @since NEXT_RELEASE *) + +val fold_filter_map : f:('acc -> 'a -> 'acc * 'b option) -> init:'acc -> 'a t -> 'b t +(** [fold_filter_map f acc l] is a {!fold_map}-like function, but the + function can choose to skip an element by retuning [None]. + @since NEXT_RELEASE *) + val map : f:('a -> 'b) -> 'a t -> 'b t (** Map objects of the sequence into other elements, lazily *) @@ -105,6 +120,20 @@ val mem : ?eq:('a -> 'a -> bool) -> x:'a -> 'a t -> bool val find : f:('a -> 'b option) -> 'a t -> 'b option (** Find the first element on which the function doesn't return [None] *) +val findi : f:(int -> 'a -> 'b option) -> 'a t -> 'b option +(** Indexed version of {!find} + @since NEXT_RELEASE *) + +val find_pred : f:('a -> bool) -> 'a t -> 'a option +(** [find_pred p l] finds the first element of [l] that satisfies [p], + or returns [None] if no element satisfies [p] + @since NEXT_RELEASE *) + +val find_pred_exn : f:('a -> bool) -> 'a t -> 'a +(** Unsafe version of {!find_pred} + @raise Not_found if no such element is found + @since NEXT_RELEASE *) + val length : 'a t -> int (** How long is the sequence? Forces the sequence. *) @@ -168,6 +197,10 @@ val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t (** Sort the sequence and remove duplicates. Eager, same as [sort] *) +val sorted : ?cmp:('a -> 'a -> int) -> 'a t -> bool +(** Checks whether the sequence is sorted. Eager, same as {!sort}. + @since NEXT_RELEASE *) + val group_succ_by : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t (** Group equal consecutive elements. Formerly synonym to [group]. @@ -189,6 +222,11 @@ val product : 'a t -> 'b t -> ('a * 'b) t as required (several times), possibly by calling {!persistent} on it beforehand. *) +val diagonal : 'a list -> ('a * 'a) t +(** All pairs of distinct positions of the list. [diagonal l] will + return the list of [List.nth i l, List.nth j l] if [i < j]. + @since NEXT_RELEASE *) + val product2 : 'a t -> 'b t -> ('a, 'b) t2 (** Binary version of {!product}. Same requirements. *) @@ -382,6 +420,10 @@ val int_range_by : step:int -> start:int -> stop:int -> int t @since NEXT_RELEASE @raise Invalid_argument if [step=0] *) +val bools : bool t +(** Iterates on [true] and [false] + @since NEXT_RELEASE *) + val of_set : (module Set.S with type elt = 'a and type t = 'b) -> 'b -> 'a t (** Convert the given set to a sequence. The set module must be provided. *) From 67a40a488271c2cd991bf889529bd40576ad14f6 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 26 Dec 2016 00:51:45 +0100 Subject: [PATCH 8/9] distinction `diagonal,diagonal_l` --- src/sequence.ml | 15 ++++++++++++--- src/sequence.mli | 9 +++++++-- src/sequenceLabels.mli | 9 +++++++-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/sequence.ml b/src/sequence.ml index 1e38e3b..425bf8d 100644 --- a/src/sequence.ml +++ b/src/sequence.ml @@ -463,14 +463,23 @@ let product outer inner k = let product2 outer inner k = outer (fun x -> inner (fun y -> k x y)) -let rec diagonal l yield = match l with +let rec diagonal_l l yield = match l with | [] -> () | x::tail -> List.iter (fun y -> yield (x,y)) tail; - diagonal tail yield + diagonal_l tail yield (*$= - [0,1; 0,2; 1,2] (diagonal [0;1;2] |> to_list) + [0,1; 0,2; 1,2] (diagonal_l [0;1;2] |> to_list) + *) + +let diagonal seq = + let l = ref [] in + seq (fun x -> l := x :: !l); + diagonal_l (List.rev !l) + +(*$= + [0,1; 0,2; 1,2] (of_list [0;1;2] |> diagonal |> to_list) *) let join ~join_row s1 s2 k = diff --git a/src/sequence.mli b/src/sequence.mli index bfc8ac1..ce7d276 100644 --- a/src/sequence.mli +++ b/src/sequence.mli @@ -251,9 +251,14 @@ val product : 'a t -> 'b t -> ('a * 'b) t as required (several times), possibly by calling {!persistent} on it beforehand. *) -val diagonal : 'a list -> ('a * 'a) t +val diagonal_l : 'a list -> ('a * 'a) t (** All pairs of distinct positions of the list. [diagonal l] will - return the list of [List.nth i l, List.nth j l] if [i < j]. + return the sequence of all [List.nth i l, List.nth j l] if [i < j]. + @since NEXT_RELEASE *) + +val diagonal : 'a t -> ('a * 'a) t +(** All pairs of distinct positions of the sequence. + Iterates only once on the sequence, which must be finite. @since NEXT_RELEASE *) val product2 : 'a t -> 'b t -> ('a, 'b) t2 diff --git a/src/sequenceLabels.mli b/src/sequenceLabels.mli index 6ef97f3..dd80e73 100644 --- a/src/sequenceLabels.mli +++ b/src/sequenceLabels.mli @@ -222,9 +222,14 @@ val product : 'a t -> 'b t -> ('a * 'b) t as required (several times), possibly by calling {!persistent} on it beforehand. *) -val diagonal : 'a list -> ('a * 'a) t +val diagonal_l : 'a list -> ('a * 'a) t (** All pairs of distinct positions of the list. [diagonal l] will - return the list of [List.nth i l, List.nth j l] if [i < j]. + return the sequence of all [List.nth i l, List.nth j l] if [i < j]. + @since NEXT_RELEASE *) + +val diagonal : 'a t -> ('a * 'a) t +(** All pairs of distinct positions of the sequence. + Iterates only once on the sequence, which must be finite. @since NEXT_RELEASE *) val product2 : 'a t -> 'b t -> ('a, 'b) t2 From 7961e625c6e9402c51a6eab93cc16433399a1c41 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 26 Dec 2016 01:21:15 +0100 Subject: [PATCH 9/9] prepare for 0.9 --- Makefile | 4 ++-- _oasis | 2 +- opam | 2 +- src/sequence.mli | 20 ++++++++++---------- src/sequenceLabels.mli | 24 ++++++++++++------------ 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index 69c58f4..6606084 100644 --- a/Makefile +++ b/Makefile @@ -77,8 +77,8 @@ VERSION=$(shell awk '/^Version:/ {print $$2}' _oasis) update_next_tag: @echo "update version to $(VERSION)..." - sed -i "s/NEXT_VERSION/$(VERSION)/g" *.ml *.mli - sed -i "s/NEXT_RELEASE/$(VERSION)/g" *.ml *.mli + sed -i "s/NEXT_VERSION/$(VERSION)/g" src/*.ml src/*.mli + sed -i "s/NEXT_RELEASE/$(VERSION)/g" src/*.ml src/*.mli watch: while find src/ -print0 | xargs -0 inotifywait -e delete_self -e modify ; do \ diff --git a/_oasis b/_oasis index 51f1ac9..eb4824c 100644 --- a/_oasis +++ b/_oasis @@ -1,6 +1,6 @@ OASISFormat: 0.4 Name: sequence -Version: 0.7 +Version: 0.9 Homepage: https://github.com/c-cube/sequence Authors: Simon Cruanes License: BSD-2-clause diff --git a/opam b/opam index a04d1ef..4b4684e 100644 --- a/opam +++ b/opam @@ -1,6 +1,6 @@ opam-version: "1.2" name: "sequence" -version: "0.7" +version: "0.9" author: "Simon Cruanes" maintainer: "simon.cruanes@inria.fr" license: "BSD-2-clauses" diff --git a/src/sequence.mli b/src/sequence.mli index ce7d276..e7c5e3d 100644 --- a/src/sequence.mli +++ b/src/sequence.mli @@ -59,7 +59,7 @@ val doubleton : 'a -> 'a -> 'a t val init : (int -> 'a) -> 'a t (** [init f] is the infinite sequence [f 0; f 1; f 2; …]. - @since NEXT_RELEASE *) + @since 0.9 *) val cons : 'a -> 'a t -> 'a t (** [cons x l] yields [x], then yields from [l]. @@ -111,12 +111,12 @@ val fold_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a t -> 'b t (** [fold_map f acc l] is like {!map}, but it carries some state as in {!fold}. The state is not returned, it is just used to thread some information to the map function. - @since NEXT_RELEASE *) + @since 0.9 *) val fold_filter_map : ('acc -> 'a -> 'acc * 'b option) -> 'acc -> 'a t -> 'b t (** [fold_filter_map f acc l] is a {!fold_map}-like function, but the function can choose to skip an element by retuning [None]. - @since NEXT_RELEASE *) + @since 0.9 *) val map : ('a -> 'b) -> 'a t -> 'b t (** Map objects of the sequence into other elements, lazily *) @@ -146,17 +146,17 @@ val find : ('a -> 'b option) -> 'a t -> 'b option val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option (** Indexed version of {!find} - @since NEXT_RELEASE *) + @since 0.9 *) val find_pred : ('a -> bool) -> 'a t -> 'a option (** [find_pred p l] finds the first element of [l] that satisfies [p], or returns [None] if no element satisfies [p] - @since NEXT_RELEASE *) + @since 0.9 *) val find_pred_exn : ('a -> bool) -> 'a t -> 'a (** Unsafe version of {!find_pred} @raise Not_found if no such element is found - @since NEXT_RELEASE *) + @since 0.9 *) val length : 'a t -> int (** How long is the sequence? Forces the sequence. *) @@ -187,7 +187,7 @@ val flat_map : ('a -> 'b t) -> 'a t -> 'b t val flat_map_l : ('a -> 'b list) -> 'a t -> 'b t (** Convenience function combining {!flat_map} and {!of_list} - @since NEXT_RELEASE *) + @since 0.9 *) val filter_map : ('a -> 'b option) -> 'a t -> 'b t (** Map and only keep non-[None] elements @@ -228,7 +228,7 @@ val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t val sorted : ?cmp:('a -> 'a -> int) -> 'a t -> bool (** Checks whether the sequence is sorted. Eager, same as {!sort}. - @since NEXT_RELEASE *) + @since 0.9 *) val group_succ_by : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t (** Group equal consecutive elements. @@ -254,12 +254,12 @@ val product : 'a t -> 'b t -> ('a * 'b) t val diagonal_l : 'a list -> ('a * 'a) t (** All pairs of distinct positions of the list. [diagonal l] will return the sequence of all [List.nth i l, List.nth j l] if [i < j]. - @since NEXT_RELEASE *) + @since 0.9 *) val diagonal : 'a t -> ('a * 'a) t (** All pairs of distinct positions of the sequence. Iterates only once on the sequence, which must be finite. - @since NEXT_RELEASE *) + @since 0.9 *) val product2 : 'a t -> 'b t -> ('a, 'b) t2 (** Binary version of {!product}. Same requirements. diff --git a/src/sequenceLabels.mli b/src/sequenceLabels.mli index dd80e73..06bd60f 100644 --- a/src/sequenceLabels.mli +++ b/src/sequenceLabels.mli @@ -37,7 +37,7 @@ val doubleton : 'a -> 'a -> 'a t val init : f:(int -> 'a) -> 'a t (** [init f] is the infinite sequence [f 0; f 1; f 2; …]. - @since NEXT_RELEASE *) + @since 0.9 *) val cons : 'a -> 'a t -> 'a t (** [cons x l] yields [x], then yields from [l]. @@ -89,12 +89,12 @@ val fold_map : f:('acc -> 'a -> 'acc * 'b) -> init:'acc -> 'a t -> 'b t (** [fold_map f acc l] is like {!map}, but it carries some state as in {!fold}. The state is not returned, it is just used to thread some information to the map function. - @since NEXT_RELEASE *) + @since 0.9 *) val fold_filter_map : f:('acc -> 'a -> 'acc * 'b option) -> init:'acc -> 'a t -> 'b t (** [fold_filter_map f acc l] is a {!fold_map}-like function, but the function can choose to skip an element by retuning [None]. - @since NEXT_RELEASE *) + @since 0.9 *) val map : f:('a -> 'b) -> 'a t -> 'b t (** Map objects of the sequence into other elements, lazily *) @@ -122,17 +122,17 @@ val find : f:('a -> 'b option) -> 'a t -> 'b option val findi : f:(int -> 'a -> 'b option) -> 'a t -> 'b option (** Indexed version of {!find} - @since NEXT_RELEASE *) + @since 0.9 *) val find_pred : f:('a -> bool) -> 'a t -> 'a option (** [find_pred p l] finds the first element of [l] that satisfies [p], or returns [None] if no element satisfies [p] - @since NEXT_RELEASE *) + @since 0.9 *) val find_pred_exn : f:('a -> bool) -> 'a t -> 'a (** Unsafe version of {!find_pred} @raise Not_found if no such element is found - @since NEXT_RELEASE *) + @since 0.9 *) val length : 'a t -> int (** How long is the sequence? Forces the sequence. *) @@ -160,7 +160,7 @@ val flat_map : f:('a -> 'b t) -> 'a t -> 'b t val flat_map_l : f:('a -> 'b list) -> 'a t -> 'b t (** Convenience function combining {!flat_map} and {!of_list} - @since NEXT_RELEASE *) + @since 0.9 *) val filter_map : f:('a -> 'b option) -> 'a t -> 'b t (** Alias to {!fmap} with a more explicit name *) @@ -199,7 +199,7 @@ val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t val sorted : ?cmp:('a -> 'a -> int) -> 'a t -> bool (** Checks whether the sequence is sorted. Eager, same as {!sort}. - @since NEXT_RELEASE *) + @since 0.9 *) val group_succ_by : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t (** Group equal consecutive elements. @@ -225,12 +225,12 @@ val product : 'a t -> 'b t -> ('a * 'b) t val diagonal_l : 'a list -> ('a * 'a) t (** All pairs of distinct positions of the list. [diagonal l] will return the sequence of all [List.nth i l, List.nth j l] if [i < j]. - @since NEXT_RELEASE *) + @since 0.9 *) val diagonal : 'a t -> ('a * 'a) t (** All pairs of distinct positions of the sequence. Iterates only once on the sequence, which must be finite. - @since NEXT_RELEASE *) + @since 0.9 *) val product2 : 'a t -> 'b t -> ('a, 'b) t2 (** Binary version of {!product}. Same requirements. *) @@ -422,12 +422,12 @@ val int_range_by : step:int -> start:int -> stop:int -> int t (** [int_range_by ~step ~start:i ~stop:j] is the range starting at [i], including [j], where the difference between successive elements is [step]. use a negative [step] for a decreasing sequence. - @since NEXT_RELEASE + @since 0.9 @raise Invalid_argument if [step=0] *) val bools : bool t (** Iterates on [true] and [false] - @since NEXT_RELEASE *) + @since 0.9 *) val of_set : (module Set.S with type elt = 'a and type t = 'b) -> 'b -> 'a t (** Convert the given set to a sequence. The set module must be provided. *)