diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 0afdf806..677e85ba 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,8 +1,19 @@ = Changelog +== 0.18 + +- update implem of `CCVector.equal` +- add `CCOpt.get_or` with label, deprecates `get` +- add `CCArray.get_safe` (close #70) +- add `CCGraph.is_dag` +- add aliases to deprecated functions from `String`, add `Fun.opaque_identity` +- add `CCLazy_list.take` +- add `Lazy_list.filter` +- add CCList.range_by + == 0.17 -=== potentially breaking +=== potentially breaking - change the semantics of `CCString.find_all` (allow overlaps) diff --git a/HOWTO.adoc b/HOWTO.adoc index 528d72e8..de85e04c 100644 --- a/HOWTO.adoc +++ b/HOWTO.adoc @@ -16,7 +16,8 @@ can be removed. 8. `git merge master` 9. `oasis setup; make test doc` 10. tag, and push both to github -11. new opam package +11. `opam pin https://github.com/c-cube/ocaml-containers#` +12. new opam package: `opam publish prepare; opam publish submit` == List Authors diff --git a/README.adoc b/README.adoc index 8787436d..b0180351 100644 --- a/README.adoc +++ b/README.adoc @@ -2,10 +2,11 @@ :toc: macro :source-highlighter: pygments -image::media/logo.png[logo] - What is _containers_? (take a look at the link:TUTORIAL.adoc[tutorial]! or the http://cedeela.fr/~simon/software/containers[documentation]) +In `containers` and `containers.data`, all modules abide by +_pay for what you use_: only modules that are used are linked (there are no +cross-module dependencies). - A usable, reasonably well-designed library that extends OCaml's standard library (in 'src/core/', packaged under `containers` in ocamlfind. Modules @@ -42,6 +43,8 @@ image:https://ci.cedeela.fr/buildStatus/icon?job=containers[alt="Build Status", toc::[] +image::media/logo.png[logo] + == Change Log See link:CHANGELOG.adoc[this file]. @@ -268,3 +271,6 @@ A few guidelines: Powered by image:http://oasis.forge.ocamlcore.org/oasis-badge.png[alt="OASIS", style="border: none;", link="http://oasis.forge.ocamlcore.org/"] +== Documentation by version + +- http://c-cube.github.io/ocaml-containers/0.17/[0.17] diff --git a/_oasis b/_oasis index 230391b9..b8d14103 100644 --- a/_oasis +++ b/_oasis @@ -1,6 +1,6 @@ OASISFormat: 0.4 Name: containers -Version: 0.17 +Version: 0.18 Homepage: https://github.com/c-cube/ocaml-containers Authors: Simon Cruanes License: BSD-2-clause @@ -40,7 +40,7 @@ Flag "bigarray" Default: true Flag "advanced" - Description: Build advanced combinators, including CCLinq (requires "sequence") + Description: Build advanced combinators (requires "sequence") Default: true Library "containers" diff --git a/doc/intro.txt b/doc/intro.txt index 6e69309d..b4b2bd73 100644 --- a/doc/intro.txt +++ b/doc/intro.txt @@ -22,6 +22,8 @@ by ocamlfind). {4 Core Modules (extension of the standard library)} +{b findlib name}: containers + {!modules: CCArray CCBool @@ -63,6 +65,8 @@ such as: {4 Containers.data} +{b findlib name}: containers.data + Various data structures. {!modules: @@ -123,6 +127,8 @@ CCLazy_list} {4 String} +{b findlib name}: containers.string + {!modules: CCApp_parse CCKMP @@ -139,6 +145,8 @@ Use bigarrays to hold large strings and map files directly into memory. {4 Advanced} +{b findlib name}: containers.advanced + This module is qualified with [Containers_advanced]. It requires {{:https://github.com/c-cube/sequence} Sequence}. @@ -152,7 +160,11 @@ Moved to its own repository. Moved to its own repository -{4 Others} +{4 Thread Helpers} + +{b findlib name}: containers.thread + +Modules related to the use of [Thread]. {!modules: CCBlockingQueue diff --git a/myocamlbuild.ml b/myocamlbuild.ml index f6541c4b..88d76fbe 100644 --- a/myocamlbuild.ml +++ b/myocamlbuild.ml @@ -1,5 +1,5 @@ (* OASIS_START *) -(* DO NOT EDIT (digest: b119194f5742ac2f3cdceac9a223dda7) *) +(* DO NOT EDIT (digest: 9ba607f1a3e839f1c1e8ea449ddfab16) *) module OASISGettext = struct (* # 22 "src/oasis/OASISGettext.ml" *) @@ -29,6 +29,166 @@ module OASISGettext = struct end +module OASISString = struct +(* # 22 "src/oasis/OASISString.ml" *) + + + (** Various string utilities. + + Mostly inspired by extlib and batteries ExtString and BatString libraries. + + @author Sylvain Le Gall + *) + + + let nsplitf str f = + if str = "" then + [] + else + let buf = Buffer.create 13 in + let lst = ref [] in + let push () = + lst := Buffer.contents buf :: !lst; + Buffer.clear buf + in + let str_len = String.length str in + for i = 0 to str_len - 1 do + if f str.[i] then + push () + else + Buffer.add_char buf str.[i] + done; + push (); + List.rev !lst + + + (** [nsplit c s] Split the string [s] at char [c]. It doesn't include the + separator. + *) + let nsplit str c = + nsplitf str ((=) c) + + + let find ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + while !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + what_idx := 0; + incr str_idx + done; + if !what_idx <> String.length what then + raise Not_found + else + !str_idx - !what_idx + + + let sub_start str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str len (str_len - len) + + + let sub_end ?(offset=0) str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str 0 (str_len - len) + + + let starts_with ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + let ok = ref true in + while !ok && + !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + ok := false; + incr str_idx + done; + if !what_idx = String.length what then + true + else + false + + + let strip_starts_with ~what str = + if starts_with ~what str then + sub_start str (String.length what) + else + raise Not_found + + + let ends_with ~what ?(offset=0) str = + let what_idx = ref ((String.length what) - 1) in + let str_idx = ref ((String.length str) - 1) in + let ok = ref true in + while !ok && + offset <= !str_idx && + 0 <= !what_idx do + if str.[!str_idx] = what.[!what_idx] then + decr what_idx + else + ok := false; + decr str_idx + done; + if !what_idx = -1 then + true + else + false + + + let strip_ends_with ~what str = + if ends_with ~what str then + sub_end str (String.length what) + else + raise Not_found + + + let replace_chars f s = + let buf = Buffer.create (String.length s) in + String.iter (fun c -> Buffer.add_char buf (f c)) s; + Buffer.contents buf + + let lowercase_ascii = + replace_chars + (fun c -> + if (c >= 'A' && c <= 'Z') then + Char.chr (Char.code c + 32) + else + c) + + let uncapitalize_ascii s = + if s <> "" then + (lowercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + + let uppercase_ascii = + replace_chars + (fun c -> + if (c >= 'a' && c <= 'z') then + Char.chr (Char.code c - 32) + else + c) + + let capitalize_ascii s = + if s <> "" then + (uppercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + +end + module OASISExpr = struct (* # 22 "src/oasis/OASISExpr.ml" *) @@ -129,7 +289,7 @@ module OASISExpr = struct end -# 132 "myocamlbuild.ml" +# 292 "myocamlbuild.ml" module BaseEnvLight = struct (* # 22 "src/base/BaseEnvLight.ml" *) @@ -234,7 +394,7 @@ module BaseEnvLight = struct end -# 237 "myocamlbuild.ml" +# 397 "myocamlbuild.ml" module MyOCamlbuildFindlib = struct (* # 22 "src/plugins/ocamlbuild/MyOCamlbuildFindlib.ml" *) @@ -516,7 +676,7 @@ module MyOCamlbuildBase = struct | nm, [], intf_modules -> ocaml_lib nm; let cmis = - List.map (fun m -> (String.uncapitalize m) ^ ".cmi") + List.map (fun m -> (OASISString.uncapitalize_ascii m) ^ ".cmi") intf_modules in dep ["ocaml"; "link"; "library"; "file:"^nm^".cma"] cmis | nm, dir :: tl, intf_modules -> @@ -529,7 +689,7 @@ module MyOCamlbuildBase = struct ["compile"; "infer_interface"; "doc"]) tl; let cmis = - List.map (fun m -> dir^"/"^(String.uncapitalize m)^".cmi") + List.map (fun m -> dir^"/"^(OASISString.uncapitalize_ascii m)^".cmi") intf_modules in dep ["ocaml"; "link"; "library"; "file:"^dir^"/"^nm^".cma"] cmis) @@ -603,7 +763,7 @@ module MyOCamlbuildBase = struct end -# 606 "myocamlbuild.ml" +# 766 "myocamlbuild.ml" open Ocamlbuild_plugin;; let package_default = { @@ -669,7 +829,7 @@ let conf = {MyOCamlbuildFindlib.no_automatic_syntax = false} let dispatch_default = MyOCamlbuildBase.dispatch_default conf package_default;; -# 673 "myocamlbuild.ml" +# 833 "myocamlbuild.ml" (* OASIS_STOP *) let doc_intro = "doc/intro.txt" ;; diff --git a/opam b/opam index 679e5df9..1185f504 100644 --- a/opam +++ b/opam @@ -26,6 +26,7 @@ remove: [ ] depends: [ "ocamlfind" {build} + "oasis" {build} "base-bytes" "result" "cppo" {build} diff --git a/setup.ml b/setup.ml index 46f2f0f3..f9920ad2 100644 --- a/setup.ml +++ b/setup.ml @@ -1,9 +1,9 @@ (* setup.ml generated for the first time by OASIS v0.4.4 *) (* OASIS_START *) -(* DO NOT EDIT (digest: 93504d34b391fe80e66c77fd2e99f4e0) *) +(* DO NOT EDIT (digest: 935c9eb827a9a1a9b4de988f1afb58dd) *) (* - Regenerated by OASIS v0.4.5 + Regenerated by OASIS v0.4.6 Visit http://oasis.forge.ocamlcore.org for more information and documentation about functions used in this file. *) @@ -246,6 +246,33 @@ module OASISString = struct String.iter (fun c -> Buffer.add_char buf (f c)) s; Buffer.contents buf + let lowercase_ascii = + replace_chars + (fun c -> + if (c >= 'A' && c <= 'Z') then + Char.chr (Char.code c + 32) + else + c) + + let uncapitalize_ascii s = + if s <> "" then + (lowercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + + let uppercase_ascii = + replace_chars + (fun c -> + if (c >= 'a' && c <= 'z') then + Char.chr (Char.code c - 32) + else + c) + + let capitalize_ascii s = + if s <> "" then + (uppercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s end @@ -315,19 +342,15 @@ module OASISUtils = struct let compare_csl s1 s2 = - String.compare (String.lowercase s1) (String.lowercase s2) + String.compare (OASISString.lowercase_ascii s1) (OASISString.lowercase_ascii s2) module HashStringCsl = Hashtbl.Make (struct type t = string - - let equal s1 s2 = - (String.lowercase s1) = (String.lowercase s2) - - let hash s = - Hashtbl.hash (String.lowercase s) + let equal s1 s2 = (compare_csl s1 s2) = 0 + let hash s = Hashtbl.hash (OASISString.lowercase_ascii s) end) module SetStringCsl = @@ -365,7 +388,7 @@ module OASISUtils = struct else buf in - String.lowercase buf + OASISString.lowercase_ascii buf end @@ -471,7 +494,7 @@ module PropList = struct order = Queue.create (); name_norm = (if case_insensitive then - String.lowercase + OASISString.lowercase_ascii else fun s -> s); } @@ -1822,13 +1845,13 @@ module OASISUnixPath = struct let capitalize_file f = let dir = dirname f in let base = basename f in - concat dir (String.capitalize base) + concat dir (OASISString.capitalize_ascii base) let uncapitalize_file f = let dir = dirname f in let base = basename f in - concat dir (String.uncapitalize base) + concat dir (OASISString.uncapitalize_ascii base) end @@ -2890,7 +2913,7 @@ module OASISFileUtil = struct end -# 2893 "setup.ml" +# 2916 "setup.ml" module BaseEnvLight = struct (* # 22 "src/base/BaseEnvLight.ml" *) @@ -2995,7 +3018,7 @@ module BaseEnvLight = struct end -# 2998 "setup.ml" +# 3021 "setup.ml" module BaseContext = struct (* # 22 "src/base/BaseContext.ml" *) @@ -5406,7 +5429,7 @@ module BaseSetup = struct end -# 5409 "setup.ml" +# 5432 "setup.ml" module InternalConfigurePlugin = struct (* # 22 "src/plugins/internal/InternalConfigurePlugin.ml" *) @@ -5845,8 +5868,8 @@ module InternalInstallPlugin = struct let make_fnames modul sufx = List.fold_right begin fun sufx accu -> - (String.capitalize modul ^ sufx) :: - (String.uncapitalize modul ^ sufx) :: + (OASISString.capitalize_ascii modul ^ sufx) :: + (OASISString.uncapitalize_ascii modul ^ sufx) :: accu end sufx @@ -6270,7 +6293,7 @@ module InternalInstallPlugin = struct end -# 6273 "setup.ml" +# 6296 "setup.ml" module OCamlbuildCommon = struct (* # 22 "src/plugins/ocamlbuild/OCamlbuildCommon.ml" *) @@ -6648,7 +6671,7 @@ module OCamlbuildDocPlugin = struct end -# 6651 "setup.ml" +# 6674 "setup.ml" module CustomPlugin = struct (* # 22 "src/plugins/custom/CustomPlugin.ml" *) @@ -6796,7 +6819,7 @@ module CustomPlugin = struct end -# 6799 "setup.ml" +# 6822 "setup.ml" open OASISTypes;; let setup_t = @@ -6875,7 +6898,7 @@ let setup_t = alpha_features = ["ocamlbuild_more_args"]; beta_features = []; name = "containers"; - version = "0.17"; + version = "0.18"; license = OASISLicense.DEP5License (OASISLicense.DEP5Unit @@ -6987,7 +7010,7 @@ let setup_t = { flag_description = Some - "Build advanced combinators, including CCLinq (requires \"sequence\")"; + "Build advanced combinators (requires \"sequence\")"; flag_default = [(OASISExpr.EBool true, true)] }); Library @@ -7721,8 +7744,9 @@ let setup_t = plugin_data = [] }; oasis_fn = Some "_oasis"; - oasis_version = "0.4.5"; - oasis_digest = Some "\168\138o\130\169\030i2!\170\1730n\148\174\208"; + oasis_version = "0.4.6"; + oasis_digest = + Some "\006\139\194\135\189\021\197\018\208\031.\187\212'\016\192"; oasis_exec = None; oasis_setup_args = []; setup_update = false @@ -7730,6 +7754,6 @@ let setup_t = let setup () = BaseSetup.setup setup_t;; -# 7734 "setup.ml" +# 7758 "setup.ml" (* OASIS_STOP *) let () = setup ();; diff --git a/src/advanced/CCLinq.mli b/src/advanced/CCLinq.mli index 898eea54..02136979 100644 --- a/src/advanced/CCLinq.mli +++ b/src/advanced/CCLinq.mli @@ -33,7 +33,7 @@ Functions and operations are assumed to be referentially transparent, i.e. they should not rely on external side effects, they should not rely on the order of execution. -@deprecated use {{: https://github.com/c-cube/olinq} OLinq} (once released) +@deprecated use {{: https://github.com/c-cube/olinq} OLinq} {[ diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index 848952a6..a7230a77 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -22,6 +22,10 @@ module type S = sig val get : 'a t -> int -> 'a + val get_safe : 'a t -> int -> 'a option + (** [get_safe a i] returns [Some a.(i)] if [i] is a valid index + @since 0.18 *) + val set : 'a t -> int -> 'a -> unit val length : _ t -> int @@ -291,6 +295,21 @@ let length = Array.length let get = Array.get +let get_safe a i = + if i>=0 && i=a.j then invalid_arg "Array.Sub.get"; a.arr.(j) + let get_safe a i = + try Some (get a i) + with Invalid_argument _ -> None + + (*$inject + let sub_a = Sub.make [|1;2;3;4;5|] 1 ~len:3 + *) + + (*$= + (Some 2) (Sub.get_safe sub_a 0) + (Some 3) (Sub.get_safe sub_a 1) + (Some 4) (Sub.get_safe sub_a 2) + None (Sub.get_safe sub_a 4) + None (Sub.get_safe sub_a max_int) + None (Sub.get_safe sub_a ~-1) + None (Sub.get_safe sub_a ~-42) + *) + let set a i x = let j = a.i + i in if i<0 || j>=a.j then invalid_arg "Array.Sub.set"; diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 29157eb7..eb6b9d29 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -24,6 +24,10 @@ module type S = sig val get : 'a t -> int -> 'a + val get_safe : 'a t -> int -> 'a option + (** [get_safe a i] returns [Some a.(i)] if [i] is a valid index + @since 0.18 *) + val set : 'a t -> int -> 'a -> unit val length : _ t -> int diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index 45d4aafb..7d2fd22b 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -37,6 +37,9 @@ val arrayi : ?start:string -> ?stop:string -> ?sep:string -> val seq : ?start:string -> ?stop:string -> ?sep:string -> 'a printer -> 'a sequence printer val opt : 'a printer -> 'a option printer +(** [opt pp] prints options as follows: + [Some x] will become "some foo" if [pp x ---> "foo"] + [None] will become "none" *) (** In the tuple printers, the [sep] argument is only available @since 0.17 *) diff --git a/src/core/CCFun.cppo.ml b/src/core/CCFun.cppo.ml index c14cdb84..b8927c81 100644 --- a/src/core/CCFun.cppo.ml +++ b/src/core/CCFun.cppo.ml @@ -15,6 +15,16 @@ let (@@) f x = f x #endif +#if OCAML_MAJOR >= 4 && OCAML_MINOR >= 3 + +let opaque_identity = Sys.opaque_identity + +#else + +let opaque_identity x = x + +#endif + let compose f g x = g (f x) let compose_binop f g x y = g (f x) (f y) diff --git a/src/core/CCFun.mli b/src/core/CCFun.mli index 7d731708..8ff1f20d 100644 --- a/src/core/CCFun.mli +++ b/src/core/CCFun.mli @@ -65,6 +65,12 @@ val finally2 : h:(unit -> _) -> ('a -> 'b -> 'c) -> 'a -> 'b -> 'c [h ()] is called whether [f x y] rose an exception or not. @since 0.16 *) +val opaque_identity : 'a -> 'a +(** [opaque_identity x] is like [x], but prevents Flambda from using [x]'s + definition for optimizing it (flambda is an optimization/inlining pass + in OCaml >= 4.03). + @since 0.18 *) + (** {2 Monad} Functions with a fixed domain are monads in their codomain *) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 857512ce..92c2747d 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -792,6 +792,38 @@ module Idx = struct *) end +let range_by ~step i j = + let rec range i j acc = + if i=j then i::acc else range i (j-step) (j::acc) + in + if step = 0 then + raise (Invalid_argument "CCList.range_by") + else if (if step > 0 then i>j else i \ + let i = min i j and j = max i j in \ + range_by ~step:1 i j = range i j) +*) + let range i j = let rec up i j acc = if i=j then i::acc else up i (j-1) (j::acc) diff --git a/src/core/CCList.mli b/src/core/CCList.mli index 41f4e5d0..a3a35b26 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -275,6 +275,13 @@ end (** {2 Other Constructors} *) +val range_by : step:int -> int -> int -> int t +(** [range_by ~step i j] iterates on integers from [i] to [j] included, + where the difference between successive elements is [step]. + use a negative [step] for a decreasing list. + @raise Invalid_argument if [step=0] + @since 0.18 *) + val range : int -> int -> int t (** [range i j] iterates on integers from [i] to [j] included . It works both for decreasing and increasing ranges *) diff --git a/src/core/CCOpt.ml b/src/core/CCOpt.ml index b15d93d2..3413e18d 100644 --- a/src/core/CCOpt.ml +++ b/src/core/CCOpt.ml @@ -93,6 +93,10 @@ let get default x = match x with | None -> default | Some y -> y +let get_or ~default x = match x with + | None -> default + | Some y -> y + let get_exn = function | Some x -> x | None -> invalid_arg "CCOpt.get_exn" diff --git a/src/core/CCOpt.mli b/src/core/CCOpt.mli index feca2f3b..cb2242ba 100644 --- a/src/core/CCOpt.mli +++ b/src/core/CCOpt.mli @@ -27,7 +27,7 @@ val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool val return : 'a -> 'a t -(** Monadic return *) +(** Monadic return, that is [return x = Some x] *) val (>|=) : 'a t -> ('a -> 'b) -> 'b t (** Infix version of {!map} *) @@ -62,7 +62,13 @@ val for_all : ('a -> bool) -> 'a t -> bool val get : 'a -> 'a t -> 'a (** [get default x] unwraps [x], but if [x = None] it returns [default] instead. - @since 0.4.1 *) + @since 0.4.1 + @deprecated use {!get_or} @since 0.18 *) + +val get_or : default:'a -> 'a t -> 'a +(** [get_or ~default o] extracts the value from [o], or + returns [default] if [o = None]. + @since 0.18 *) val get_exn : 'a t -> 'a (** Open the option, possibly failing if it is [None] @@ -103,6 +109,7 @@ val (<+>) : 'a t -> 'a t -> 'a t val choice : 'a t list -> 'a t (** [choice] returns the first non-[None] element of the list, or [None] *) + (** {2 Infix Operators} @since 0.16 *) diff --git a/src/core/CCString.cppo.ml b/src/core/CCString.cppo.ml index b476f92f..8a510d4e 100644 --- a/src/core/CCString.cppo.ml +++ b/src/core/CCString.cppo.ml @@ -639,6 +639,26 @@ let exists2 p s1 s2 = try iter2 (fun c1 c2 -> if p c1 c2 then raise MyExit) s1 s2; false with MyExit -> true +(** {2 Ascii functions} *) + +#if OCAML_MAJOR >= 4 && OCAML_MINOR >= 3 + +let capitalize_ascii = String.capitalize_ascii +let uncapitalize_ascii = String.uncapitalize_ascii +let uppercase_ascii = String.uppercase_ascii +let lowercase_ascii = String.lowercase_ascii + +#else + +let capitalize_ascii = String.capitalize +let uncapitalize_ascii = String.uncapitalize +let uppercase_ascii = String.uppercase +let lowercase_ascii = String.lowercase + +#endif + + + let pp buf s = Buffer.add_char buf '"'; Buffer.add_string buf s; diff --git a/src/core/CCString.mli b/src/core/CCString.mli index a61d52fd..4d292fd1 100644 --- a/src/core/CCString.mli +++ b/src/core/CCString.mli @@ -378,6 +378,24 @@ val exists2 : (char -> char -> bool) -> string -> string -> bool @raise Invalid_argument if the strings have not the same length @since 0.12 *) +(** {2 Ascii functions} + + Those functions are deprecated in {!String} since 4.03, so we provide + a stable alias for them even in older versions *) + +val capitalize_ascii : string -> string +(** See {!String}. @since 0.18 *) + +val uncapitalize_ascii : string -> string +(** See {!String}. @since 0.18 *) + +val uppercase_ascii : string -> string +(** See {!String}. @since 0.18 *) + +val lowercase_ascii : string -> string +(** See {!String}. @since 0.18 *) + + (** {2 Splitting} *) module Split : sig diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index d1290cf5..40296609 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -243,12 +243,25 @@ let append_list a b = match b with *) let equal eq v1 v2 = - let n = min v1.size v2.size in + v1.size = v2.size + && + let n = v1.size in let rec check i = - if i = n - then v1.size = v2.size - else eq (get v1 i) (get v2 i) && check (i+1) - in check 0 + i = n || (eq (get v1 i) (get v2 i) && check (i+1)) + in + check 0 + +(*$T + equal (=) (create ()) (create ()) + equal (=) (return 42) (return 42) + not (equal (=) (create ()) (return 42)) + not (equal (=) (return 42) (create ())) +*) + +(*$Q + Q.(let g = list_of_size Gen.(0--10) small_int in pair g g) (fun (l1,l2) -> \ + equal (=) (of_list l1) (of_list l2) = (l1=l2)) +*) let compare cmp v1 v2 = let n = min v1.size v2.size in diff --git a/src/core/META b/src/core/META index 9f2ec31f..c5b3582c 100644 --- a/src/core/META +++ b/src/core/META @@ -1,6 +1,6 @@ # OASIS_START -# DO NOT EDIT (digest: 775c1a5da08322de06b23069a43379ed) -version = "0.17" +# DO NOT EDIT (digest: 048ee564ec86589b85b55f903119ad20) +version = "0.18" description = "A modular standard library focused on data structures." requires = "bytes result" archive(byte) = "containers.cma" @@ -9,7 +9,7 @@ archive(native) = "containers.cmxa" archive(native, plugin) = "containers.cmxs" exists_if = "containers.cma" package "unix" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "bytes unix" archive(byte) = "containers_unix.cma" @@ -20,7 +20,7 @@ package "unix" ( ) package "top" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "compiler-libs.common containers containers.data containers.bigarray containers.string containers.unix containers.sexp containers.iter" @@ -32,7 +32,7 @@ package "top" ( ) package "thread" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "containers threads" archive(byte) = "containers_thread.cma" @@ -43,7 +43,7 @@ package "thread" ( ) package "string" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_string.cma" @@ -54,7 +54,7 @@ package "string" ( ) package "sexp" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_sexp.cma" @@ -65,7 +65,7 @@ package "sexp" ( ) package "iter" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." archive(byte) = "containers_iter.cma" archive(byte, plugin) = "containers_iter.cma" @@ -75,7 +75,7 @@ package "iter" ( ) package "io" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_io.cma" @@ -86,7 +86,7 @@ package "io" ( ) package "data" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "bytes" archive(byte) = "containers_data.cma" @@ -97,7 +97,7 @@ package "data" ( ) package "bigarray" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "containers bigarray bytes" archive(byte) = "containers_bigarray.cma" @@ -108,7 +108,7 @@ package "bigarray" ( ) package "advanced" ( - version = "0.17" + version = "0.18" description = "A modular standard library focused on data structures." requires = "containers sequence" archive(byte) = "containers_advanced.cma" diff --git a/src/data/CCGraph.ml b/src/data/CCGraph.ml index 37a9da15..2155f089 100644 --- a/src/data/CCGraph.ml +++ b/src/data/CCGraph.ml @@ -43,6 +43,10 @@ module Seq = struct a (fun x -> acc := f !acc x); !acc let to_list seq = fold (fun acc x->x::acc) [] seq |> List.rev + exception Exit_ + let exists_ f seq = + try seq (fun x -> if f x then raise Exit_); false + with Exit_ -> true end (** {2 Interfaces for graphs} *) @@ -315,6 +319,15 @@ module Traverse = struct end end +(** {2 Cycles} *) + +let is_dag ?(tbl=mk_table 128) ~graph vs = + Traverse.Event.dfs ~tbl ~graph vs + |> Seq.exists_ + (function + | `Edge (_, `Back) -> true + | _ -> false) + (** {2 Topological Sort} *) exception Has_cycle diff --git a/src/data/CCGraph.mli b/src/data/CCGraph.mli index 22bc1233..5ccb00f2 100644 --- a/src/data/CCGraph.mli +++ b/src/data/CCGraph.mli @@ -224,6 +224,17 @@ module Traverse : sig end end +(** {2 Cycles} *) + +val is_dag : + ?tbl:'v set -> + graph:('v, _) t -> + 'v sequence -> + bool +(** [is_dag ~graph vs] returns [true] if the subset of [graph] reachable + from [vs] is acyclic. + @since 0.18 *) + (** {2 Topological Sort} *) exception Has_cycle diff --git a/src/data/CCTrie.ml b/src/data/CCTrie.ml index 7af7a165..569b6bdc 100644 --- a/src/data/CCTrie.ml +++ b/src/data/CCTrie.ml @@ -368,7 +368,7 @@ module Make(W : WORD) *) (*$Q - Q.(pair (list (pair printable_string int)) printable_string) (fun (l,s) -> \ + Q.(pair (list (pair (printable_string_of_size Gen.(0 -- 30)) int)) printable_string) (fun (l,s) -> \ let m = String.of_list l in \ let s' = String.longest_prefix s m in \ CCString.prefix ~pre:s' s) diff --git a/src/iter/CCLazy_list.ml b/src/iter/CCLazy_list.ml index ffaf76ce..a258f372 100644 --- a/src/iter/CCLazy_list.ml +++ b/src/iter/CCLazy_list.ml @@ -40,6 +40,27 @@ let rec map ~f l = | lazy (Cons (x,tl)) -> Cons (f x, map ~f tl) ) +let filter ~f l = + let rec aux f l = match l with + | lazy Nil -> Nil + | lazy (Cons (x,tl)) when f x -> Cons (x, lazy (aux f tl)) + | lazy (Cons (_, tl)) -> aux f tl + in + lazy (aux f l) + +(*$= + [2;4;6] (of_list [1;2;3;4;5;6;7] |> filter ~f:(fun x -> x mod 2=0) |> to_list) + [2;4;6] (of_gen Gen.(1 -- max_int) |> filter ~f:(fun x -> x mod 2=0) |> take 3 |> to_list) +*) + +let rec take n l = + lazy ( + match l with + | _ when n=0 -> Nil + | lazy Nil -> Nil + | lazy (Cons (x,tl)) -> Cons (x, take (n-1) tl) + ) + let rec append a b = lazy ( match a with diff --git a/src/iter/CCLazy_list.mli b/src/iter/CCLazy_list.mli index 6a51cd3b..3f046fb2 100644 --- a/src/iter/CCLazy_list.mli +++ b/src/iter/CCLazy_list.mli @@ -31,6 +31,14 @@ val head : 'a t -> ('a * 'a t) option val map : f:('a -> 'b) -> 'a t -> 'b t (** Lazy map *) +val filter : f:('a -> bool) -> 'a t -> 'a t +(** Filter values. + @since 0.18 *) + +val take : int -> 'a t -> 'a t +(** Take at most n values. + @since 0.18 *) + val append : 'a t -> 'a t -> 'a t (** Lazy concatenation *)