From 69f2fd37ab714a4d3ff6c31571a804f991f24200 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 7 Jul 2014 18:19:21 +0200 Subject: [PATCH 1/6] Makefile target to push into stable --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8a1e499..ce1e9de 100644 --- a/Makefile +++ b/Makefile @@ -49,10 +49,18 @@ examples: push_doc: all doc scp -r sequence.docdir/* cedeela.fr:~/simon/root/software/sequence/ +push_stable: all + git checkout stable + git merge master -m 'merge from master' + oasis setup + git commit -a -m 'oasis files' + git push origin + git checkout master + VERSION=$(shell awk '/Version:/ {print $$2}' _oasis) update_next_tag: @echo "update version to $(VERSION)..." sed -i "s/NEXT_VERSION/$(VERSION)/g" *.ml *.mli -.PHONY: benchs tests examples update_next_tag push_doc +.PHONY: benchs tests examples update_next_tag push_doc push_stable From 78fefc2178449db5a2722168359f6b00ac7da00c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 9 Jul 2014 10:22:49 +0200 Subject: [PATCH 2/6] update doc --- sequence.mli | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/sequence.mli b/sequence.mli index e2691a9..6a84dfb 100644 --- a/sequence.mli +++ b/sequence.mli @@ -23,10 +23,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) -(** {1 Transient iterators, that abstract on a finite sequence of elements.} *) +(** {1 Simple and Efficient Iterators} *) (** The iterators are designed to allow easy transfer (mappings) between data - structures, without defining n^2 conversions between the n types. The + structures, without defining [n^2] conversions between the [n] types. The implementation relies on the assumption that a sequence can be iterated on as many times as needed; this choice allows for high performance of many combinators. However, for transient iterators, the {!persistent} @@ -53,8 +53,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. of this memory structure, cheaply and repeatably. *) type +'a t = ('a -> unit) -> unit - (** Sequence abstract iterator type, representing a finite sequence of - values of type ['a]. *) + (** A sequence of values of type ['a]. If you give it a function ['a -> unit] + it will be applied to every element of the sequence successively. *) type +'a sequence = 'a t @@ -97,7 +97,7 @@ val repeat : 'a -> 'a t at {!take} and the likes if you iterate on it. *) val iterate : ('a -> 'a) -> 'a -> 'a t - (** [iterate f x] is the infinite sequence (x, f(x), f(f(x)), ...) *) + (** [iterate f x] is the infinite sequence [x, f(x), f(f(x)), ...] *) val forever : (unit -> 'b) -> 'b t (** Sequence that calls the given function to produce elements. @@ -113,7 +113,8 @@ val cycle : 'a t -> 'a t (** {2 Consume a sequence} *) val iter : ('a -> unit) -> 'a t -> unit - (** Consume the sequence, passing all its arguments to the function *) + (** Consume the sequence, passing all its arguments to the function. + Basically [iter f seq] is just [seq f]. *) val iteri : (int -> 'a -> unit) -> 'a t -> unit (** Iterate on elements and their index in the sequence *) @@ -258,7 +259,9 @@ val take : int -> 'a t -> 'a t sequences. *) val take_while : ('a -> bool) -> 'a t -> 'a t - (** Take elements while they satisfy the predicate, then stops iterating *) + (** Take elements while they satisfy the predicate, then stops iterating. + Will work on an infinite sequence [s] if the predicate is false for at + least one element of [s]. *) val drop : int -> 'a t -> 'a t (** Drop the [n] first elements of the sequence. Lazy. *) @@ -309,7 +312,7 @@ val of_list : 'a list -> 'a t val to_array : 'a t -> 'a array (** Convert to an array. Currently not very efficient because - and intermediate list is used. *) + an intermediate list is used. *) val of_array : 'a array -> 'a t @@ -482,16 +485,20 @@ module Infix : sig It will therefore be empty if [a < b]. *) val (>>=) : 'a t -> ('a -> 'b t) -> 'b t - (** Monadic bind (infix version of {!flat_map} *) + (** Monadic bind (infix version of {!flat_map} + @since 0.5 *) val (>|=) : 'a t -> ('a -> 'b) -> 'b t - (** Infix version of {!map} *) + (** Infix version of {!map} + @since 0.5 *) val (<*>) : ('a -> 'b) t -> 'a t -> 'b t - (** Applicative operator (product+application) *) + (** Applicative operator (product+application) + @since 0.5 *) val (<+>) : 'a t -> 'a t -> 'a t - (** Concatenation of sequences *) + (** Concatenation of sequences + @since 0.5 *) end include module type of Infix From 0c474eec3b797dfffd476d13fa13dc4677631807 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 9 Jul 2014 10:24:54 +0200 Subject: [PATCH 3/6] update readme --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 17febb3..0ca3219 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,16 @@ 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 +============= + +See [the online API](http://cedeela.fr/~simon/software/sequence/Sequence.html). + Build ===== -You need OCaml, say OCaml 3.12 or OCaml 4.0. - - $ make +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 @@ -40,11 +44,6 @@ 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`. -Documentation -============= - -See [the online API](http://cedeela.fr/~simon/software/sequence/Sequence.html). - License ======= From c79fa08b521b2c662b94b099badb3be6d466a260 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 17 Jul 2014 18:06:51 +0200 Subject: [PATCH 4/6] options: to_opt/of_opt/head/head_exn --- sequence.ml | 17 +++++++++++++++++ sequence.mli | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/sequence.ml b/sequence.ml index cc14f74..65edcbe 100644 --- a/sequence.ml +++ b/sequence.ml @@ -321,6 +321,17 @@ let min ?(lt=fun x y -> x < y) seq = exception ExitSequence +let head seq = + let r = ref None in + try + seq (fun x -> r := Some x; raise ExitSequence); None + with ExitSequence -> !r + +let head_exn seq = + match head seq with + | None -> invalid_arg "Sequence.head_exn" + | Some x -> x + let take n seq k = let count = ref 0 in try @@ -425,6 +436,12 @@ let to_list seq = List.rev (fold (fun y x -> x::y) [] seq) let to_rev_list seq = fold (fun y x -> x :: y) [] seq +let to_opt = head + +let of_opt o k = match o with + | None -> () + | Some x -> k x + let of_list l k = List.iter k l let to_array seq = diff --git a/sequence.mli b/sequence.mli index 6a84dfb..5d2817e 100644 --- a/sequence.mli +++ b/sequence.mli @@ -254,6 +254,15 @@ val min : ?lt:('a -> 'a -> bool) -> 'a t -> 'a option (** Min element of the sequence, using the given comparison function. see {!max} for more details. *) +val head : 'a t -> 'a option + (** First element, if any, otherwise [None] + @since NEXT_RELEASE *) + +val head_exn : 'a t -> 'a + (** First element, if any, fails + @raise Invalid_argument if the sequence is empty + @since NEXT_RELEASE *) + val take : int -> 'a t -> 'a t (** Take at most [n] elements from the sequence. Works on infinite sequences. *) @@ -310,6 +319,10 @@ val to_rev_list : 'a t -> 'a list val of_list : 'a list -> 'a t +val to_opt : 'a t -> 'a option + (** Alias to {!head} + @since NEXT_RELEASE *) + val to_array : 'a t -> 'a array (** Convert to an array. Currently not very efficient because an intermediate list is used. *) @@ -325,6 +338,10 @@ val array_slice : 'a array -> int -> int -> 'a t (** [array_slice a i j] Sequence of elements whose indexes range from [i] to [j] *) +val of_opt : 'a option -> 'a t + (** Iterate on 0 or 1 values. + @since NEXT_RELEASE *) + val of_stream : 'a Stream.t -> 'a t (** Sequence of elements of a stream (usable only once) *) From 185cf14f28d5eb3b23ac3e83016bb64629a1a9b1 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 7 Aug 2014 12:06:01 +0200 Subject: [PATCH 5/6] fix makefile.update_next_tag --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ce1e9de..db135ee 100644 --- a/Makefile +++ b/Makefile @@ -57,10 +57,11 @@ push_stable: all git push origin git checkout master -VERSION=$(shell awk '/Version:/ {print $$2}' _oasis) +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 .PHONY: benchs tests examples update_next_tag push_doc push_stable From 3458581ff25bbddbfaca694ee1f8617a46879030 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 7 Aug 2014 12:06:59 +0200 Subject: [PATCH 6/6] Sequence.IO module, a very very simple way to read/write files --- sequence.ml | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sequence.mli | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/sequence.ml b/sequence.ml index 65edcbe..9e5fb84 100644 --- a/sequence.ml +++ b/sequence.ml @@ -716,3 +716,56 @@ let to_string ?sep pp_elt seq = let buf = Buffer.create 25 in pp_buf ?sep (fun buf x -> Buffer.add_string buf (pp_elt x)) buf seq; Buffer.contents buf + +(** {2 Basic IO} *) + +module IO = struct + let lines_of ?(mode=0o644) ?(flags=[Open_rdonly]) filename = + fun k -> + let ic = open_in_gen flags mode filename in + try + while true do + let line = input_line ic in + k line + done + with + | End_of_file -> close_in ic + | e -> close_in_noerr ic; raise e + + let chunks_of ?(mode=0o644) ?(flags=[]) ?(size=1024) filename = + fun k -> + let ic = open_in_gen flags mode filename in + try + let buf = String.create size in + let n = ref 0 in + let stop = ref false in + while not !stop do + n := 0; + (* try to read [size] chars. If [input] returns [0] it means + the end of file, so we stop, but first we yield the current chunk *) + while !n < size && not !stop do + let n' = input ic buf !n (size - !n) in + if n' = 0 then stop := true else n := !n + n'; + done; + if !n > 0 + then k (String.sub buf 0 !n) + done; + close_in ic + with e -> + close_in_noerr ic; + raise e + + let write_to ?(mode=0o644) ?(flags=[Open_creat;Open_wronly]) filename seq = + let oc = open_out_gen flags mode filename in + try + seq (fun s -> output oc s 0 (String.length s)); + close_out oc + with e -> + close_out oc; + raise e + + let write_lines ?mode ?flags filename seq = + write_to ?mode ?flags filename (snoc (intersperse "\n" seq) "\n") +end + + diff --git a/sequence.mli b/sequence.mli index 5d2817e..2d8ee31 100644 --- a/sequence.mli +++ b/sequence.mli @@ -534,3 +534,54 @@ val pp_buf : ?sep:string -> (Buffer.t -> 'a -> unit) -> val to_string : ?sep:string -> ('a -> string) -> 'a t -> string (** Print into a string *) + +(** {2 Basic IO} + +Very basic interface to manipulate files as sequence of chunks/lines. The +sequences take care of opening and closing files properly; every time +one iterates over a sequence, the file is opened/closed again. + +Example: copy a file ["a"] into file ["b"], removing blank lines: + +{[ + Sequence.(IO.lines_of "a" |> filter (fun l-> l<> "") |> IO.write_lines "b");; +]} + +By chunks of [4096] bytes: + +{[ + Sequence.IO.(chunks_of ~size:4096 "a" |> write_to "b");; +]} + +@since NEXT_RELEASE *) + +module IO : sig + val lines_of : ?mode:int -> ?flags:open_flag list -> + string -> string t + (** [lines_of filename] reads all lines of the given file. It raises the + same exception as would opening the file and read from it, except + from [End_of_file] (which is caught). The file is {b always} properly + closed. + Every time the sequence is iterated on, the file is opened again, so + different iterations might return different results + @param mode default [0o644] + @param flags default: [[Open_rdonly]] *) + + val chunks_of : ?mode:int -> ?flags:open_flag list -> ?size:int -> + string -> string t + (** Read chunks of the given [size] from the file. The last chunk might be + smaller. Behaves like {!lines_of} regarding errors and options. + Every time the sequence is iterated on, the file is opened again, so + different iterations might return different results *) + + val write_to : ?mode:int -> ?flags:open_flag list -> + string -> string t -> unit + (** [write_to filename seq] writes all strings from [seq] into the given + file. It takes care of opening and closing the file. + @param mode default [0o644] + @param flags used by [open_out_gen]. Default: [[Open_creat;Open_wronly]]. *) + + val write_lines : ?mode:int -> ?flags:open_flag list -> + string -> string t -> unit + (** Same as {!write_to}, but intercales ['\n'] between each string *) +end