mirror of
https://github.com/c-cube/iter.git
synced 2025-12-09 04:35:31 -05:00
merge from master: version 0.5.1
This commit is contained in:
commit
e5625c6930
8 changed files with 178 additions and 28 deletions
|
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
## 0.5.1
|
||||
|
||||
- `Sequence.IO` module, a very very simple way to read/write files
|
||||
- options: `to_opt/of_opt/head/head_exn`
|
||||
|
||||
## 0.5
|
||||
|
||||
- conversion with `klist`
|
||||
|
|
|
|||
6
META
6
META
|
|
@ -1,6 +1,6 @@
|
|||
# OASIS_START
|
||||
# DO NOT EDIT (digest: 97bacb5c6907fa4ab239010b6f052d2a)
|
||||
version = "0.5"
|
||||
# DO NOT EDIT (digest: 3b9ebef180f5e4bdb720d2103ba95667)
|
||||
version = "0.5.1"
|
||||
description = "Simple sequence (iterator) datatype and combinators"
|
||||
archive(byte) = "sequence.cma"
|
||||
archive(byte, plugin) = "sequence.cma"
|
||||
|
|
@ -8,7 +8,7 @@ archive(native) = "sequence.cmxa"
|
|||
archive(native, plugin) = "sequence.cmxs"
|
||||
exists_if = "sequence.cma"
|
||||
package "invert" (
|
||||
version = "0.5"
|
||||
version = "0.5.1"
|
||||
description = "Simple sequence (iterator) datatype and combinators"
|
||||
requires = "sequence delimcc"
|
||||
archive(byte) = "invert.cma"
|
||||
|
|
|
|||
3
Makefile
3
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
|
||||
|
|
|
|||
15
README.md
15
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
|
||||
=======
|
||||
|
||||
|
|
|
|||
2
_oasis
2
_oasis
|
|
@ -1,6 +1,6 @@
|
|||
OASISFormat: 0.4
|
||||
Name: sequence
|
||||
Version: 0.5
|
||||
Version: 0.5.1
|
||||
Homepage: https://github.com/c-cube/sequence
|
||||
Authors: Simon Cruanes
|
||||
License: BSD-2-clause
|
||||
|
|
|
|||
70
sequence.ml
70
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 =
|
||||
|
|
@ -699,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
|
||||
|
||||
|
||||
|
|
|
|||
99
sequence.mli
99
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 *)
|
||||
|
|
@ -253,12 +254,23 @@ 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 0.5.1 *)
|
||||
|
||||
val head_exn : 'a t -> 'a
|
||||
(** First element, if any, fails
|
||||
@raise Invalid_argument if the sequence is empty
|
||||
@since 0.5.1 *)
|
||||
|
||||
val take : int -> 'a t -> 'a t
|
||||
(** Take at most [n] elements from the sequence. Works on infinite
|
||||
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. *)
|
||||
|
|
@ -307,9 +319,13 @@ 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 0.5.1 *)
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -322,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 0.5.1 *)
|
||||
|
||||
val of_stream : 'a Stream.t -> 'a t
|
||||
(** Sequence of elements of a stream (usable only once) *)
|
||||
|
||||
|
|
@ -482,16 +502,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
|
||||
|
|
@ -510,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 0.5.1 *)
|
||||
|
||||
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
|
||||
|
|
|
|||
6
setup.ml
6
setup.ml
|
|
@ -1,7 +1,7 @@
|
|||
(* setup.ml generated for the first time by OASIS v0.4.4 *)
|
||||
|
||||
(* OASIS_START *)
|
||||
(* DO NOT EDIT (digest: 623687f8c58c30bc6ff9a8e5ebe8f593) *)
|
||||
(* DO NOT EDIT (digest: d7a207daf3186cce7792651a50aaba59) *)
|
||||
(*
|
||||
Regenerated by OASIS v0.4.4
|
||||
Visit http://oasis.forge.ocamlcore.org for more information and
|
||||
|
|
@ -6826,7 +6826,7 @@ let setup_t =
|
|||
alpha_features = [];
|
||||
beta_features = [];
|
||||
name = "sequence";
|
||||
version = "0.5";
|
||||
version = "0.5.1";
|
||||
license =
|
||||
OASISLicense.DEP5License
|
||||
(OASISLicense.DEP5Unit
|
||||
|
|
@ -7192,7 +7192,7 @@ let setup_t =
|
|||
};
|
||||
oasis_fn = Some "_oasis";
|
||||
oasis_version = "0.4.4";
|
||||
oasis_digest = Some "6\2028\169\031Z \246[\195\132\t\168\226\152(";
|
||||
oasis_digest = Some "8\252\157\1340^<0\133GR\029nmc6";
|
||||
oasis_exec = None;
|
||||
oasis_setup_args = [];
|
||||
setup_update = false
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue