mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2026-01-23 09:36:41 -05:00
Merge branch 'master' into stable for 1.1
This commit is contained in:
commit
9fb319966b
17 changed files with 559 additions and 462 deletions
8
.merlin
8
.merlin
|
|
@ -1,14 +1,9 @@
|
|||
S src/core
|
||||
S src/data/
|
||||
S src/io
|
||||
S src/iter/
|
||||
S src/advanced/
|
||||
S src/lwt/
|
||||
S src/sexp/
|
||||
S src/threads/
|
||||
S src/misc
|
||||
S src/string
|
||||
S src/bigarray
|
||||
S benchs
|
||||
S examples
|
||||
S tests
|
||||
|
|
@ -22,8 +17,5 @@ PKG result
|
|||
PKG threads
|
||||
PKG threads.posix
|
||||
PKG lwt
|
||||
PKG bigarray
|
||||
PKG sequence
|
||||
PKG gen
|
||||
PKG qcheck
|
||||
FLG -w +a -w -4 -w -44
|
||||
|
|
|
|||
12
.ocamlinit
12
.ocamlinit
|
|
@ -1,33 +1,21 @@
|
|||
#use "topfind";;
|
||||
#thread
|
||||
#require "result";;
|
||||
#require "bigarray";;
|
||||
#require "unix";;
|
||||
#require "sequence";;
|
||||
#directory "_build/src/core";;
|
||||
#directory "_build/src/misc";;
|
||||
#directory "_build/src/pervasives/";;
|
||||
#directory "_build/src/string";;
|
||||
#directory "_build/src/io";;
|
||||
#directory "_build/src/unix";;
|
||||
#directory "_build/src/iter";;
|
||||
#directory "_build/src/data";;
|
||||
#directory "_build/src/advanced/";;
|
||||
#directory "_build/src/sexp";;
|
||||
#directory "_build/src/bigarray/";;
|
||||
#directory "_build/src/threads";;
|
||||
#directory "_build/src/top/";;
|
||||
#directory "_build/tests/";;
|
||||
#load "containers.cma";;
|
||||
#load "containers_iter.cma";;
|
||||
#load "containers_data.cma";;
|
||||
#load "containers_advanced.cma";;
|
||||
#load "containers_io.cma";;
|
||||
#load "containers_unix.cma";;
|
||||
#load "containers_sexp.cma";;
|
||||
#load "containers_string.cma";;
|
||||
#load "containers_pervasives.cma";;
|
||||
#load "containers_bigarray.cma";;
|
||||
#load "containers_top.cma";;
|
||||
#thread;;
|
||||
#load "containers_thread.cma";;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,26 @@
|
|||
= Changelog
|
||||
|
||||
== 1.1
|
||||
|
||||
**bugfixes**:
|
||||
|
||||
- fix bug in `CCGraph` (in DFS traversal)
|
||||
- fix bug in `CCOpt.filter` (close #100)
|
||||
|
||||
**new features**:
|
||||
|
||||
- add `CCHeap.to_seq_sorted`
|
||||
- add `CCHeap.to_list_sorted`
|
||||
- add `CCIO.File.walk_l`
|
||||
|
||||
**cleanup and doc**:
|
||||
|
||||
- remove dead code
|
||||
- new test for `CCPool`
|
||||
- new test and small readme section on `CCParse`
|
||||
- remove CCError from tutorial
|
||||
- merge tutorial into readme, cleanup
|
||||
|
||||
== 1.0
|
||||
|
||||
See https://github.com/c-cube/ocaml-containers/issues/84 for an overview.
|
||||
|
|
|
|||
471
README.adoc
471
README.adoc
|
|
@ -2,11 +2,23 @@
|
|||
:toc: macro
|
||||
:source-highlighter: pygments
|
||||
|
||||
What is _containers_? (take a look at the link:TUTORIAL.adoc[tutorial]!
|
||||
or the http://cedeela.fr/~simon/software/containers[current 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 modular, clean and powerful extension of the OCaml standard library.
|
||||
|
||||
Containers is an extension of OCaml's standard library (under BSD license)
|
||||
focused on data structures, combinators and iterators, without dependencies on
|
||||
unix, str or num. Every module is independent and is prefixed with 'CC' in the
|
||||
global namespace. Some modules extend the stdlib (e.g. CCList provides safe
|
||||
map/fold_right/append, and additional functions on lists).
|
||||
Alternatively, `open Containers` will bring enhanced versions of the standard
|
||||
modules into scope.
|
||||
|
||||
image:https://ci.cedeela.fr/buildStatus/icon?job=containers[alt="Build Status", link="http://ci.cedeela.fr/job/containers/"]
|
||||
|
||||
toc::[]
|
||||
|
||||
== Quick Summary
|
||||
|
||||
Containers is:
|
||||
|
||||
- A usable, reasonably well-designed library that extends OCaml's standard
|
||||
library (in 'src/core/', packaged under `containers` in ocamlfind. Modules
|
||||
|
|
@ -18,33 +30,26 @@ cross-module dependencies).
|
|||
`Containers` (intended to be opened, replaces some stdlib modules
|
||||
with extended ones).
|
||||
- Several small additional libraries that complement it:
|
||||
|
||||
containers.data:: with additional data structures that don't have an
|
||||
* `containers.data` with additional data structures that don't have an
|
||||
equivalent in the standard library;
|
||||
containers.iter:: with list-like and tree-like iterators;
|
||||
|
||||
- A sub-library with complicated abstractions, `containers.advanced` (with
|
||||
a LINQ-like query module, batch operations using GADTs, and others).
|
||||
* `containers.iter` with list-like and tree-like iterators;
|
||||
- Utilities around the `unix` library in `containers.unix` (mainly to spawn
|
||||
sub-processes)
|
||||
sub-processes easily and deal with resources safely)
|
||||
- A lightweight S-expression printer and streaming parser in `containers.sexp`
|
||||
- A library for threaded programming in `containers.thread`,
|
||||
including a blocking queue, semaphores, an extension of `Mutex`, and
|
||||
thread-pool based futures.
|
||||
|
||||
Some of the modules have been moved to their own repository (e.g. `sequence`,
|
||||
`gen`, `qcheck`) and are on opam for great fun and profit.
|
||||
|
||||
image:https://ci.cedeela.fr/buildStatus/icon?job=containers[alt="Build Status", link="http://ci.cedeela.fr/job/containers/"]
|
||||
|
||||
toc::[]
|
||||
|
||||
image::media/logo.png[logo]
|
||||
|
||||
== Change Log
|
||||
|
||||
See link:CHANGELOG.adoc[this file].
|
||||
|
||||
== Finding help
|
||||
|
||||
- *new*: http://lists.ocaml.org/listinfo/containers-users[Mailing List]
|
||||
- http://lists.ocaml.org/listinfo/containers-users[Mailing List]
|
||||
the address is mailto:containers-users@lists.ocaml.org[]
|
||||
- the https://github.com/c-cube/ocaml-containers/wiki[github wiki]
|
||||
- on IRC, ask `companion_cube` on `#ocaml@freenode.net`
|
||||
|
|
@ -52,7 +57,7 @@ See link:CHANGELOG.adoc[this file].
|
|||
|
||||
== Use
|
||||
|
||||
Start with the link:TUTORIAL.adoc[tutorial]
|
||||
You might start with the <<tutorial>> to get a picture of how to use the library.
|
||||
|
||||
You can either build and install the library (see <<build>>), or just copy
|
||||
files to your own project. The last solution has the benefits that you
|
||||
|
|
@ -79,141 +84,29 @@ If you have comments, requests, or bugfixes, please share them! :-)
|
|||
|
||||
This code is free, under the BSD license.
|
||||
|
||||
The logo (`media/logo.png`) is
|
||||
CC-SA3 http://en.wikipedia.org/wiki/File:Hypercube.svg[wikimedia].
|
||||
|
||||
== Contents
|
||||
|
||||
The library contains a <<core,Core part>> that mostly extends the stdlib
|
||||
and adds a few very common structures (heap, vector), and sub-libraries
|
||||
that deal with either more specific things, or require additional dependencies.
|
||||
|
||||
Some structural types are used throughout the library:
|
||||
|
||||
gen:: `'a gen = unit -> 'a option` is an iterator type. Many combinators
|
||||
are defined in the opam library https://github.com/c-cube/gen[gen]
|
||||
sequence:: `'a sequence = (unit -> 'a) -> unit` is also an iterator type.
|
||||
It is easier to define on data structures than `gen`, but it a bit less
|
||||
powerful. The opam library https://github.com/c-cube/sequence[sequence]
|
||||
can be used to consume and produce values of this type.
|
||||
error:: (DEPRECATED) `'a or_error = [`Error of string | `Ok of 'a]` is a error type
|
||||
that is used in other libraries, too. It is now deprecated and
|
||||
replaced with `('a, string) Result.result`, supported in
|
||||
`CCResult`.
|
||||
klist:: `'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]` is a lazy list
|
||||
without memoization, used as a persistent iterator. The reference
|
||||
module is `CCKList` (in `containers.iter`).
|
||||
printer:: `'a printer = Format.formatter -> 'a -> unit` is a pretty-printer
|
||||
to be used with the standard module `Format`. In particular, in many cases,
|
||||
`"foo: %a" Foo.print foo` will type-check.
|
||||
|
||||
[[core]]
|
||||
=== Core Modules (extension of the standard library)
|
||||
|
||||
the core library, `containers`, now depends on
|
||||
https://github.com/mjambon/cppo[cppo] and `base-bytes` (provided
|
||||
by ocamlfind).
|
||||
|
||||
Documentation http://cedeela.fr/~simon/software/containers[here].
|
||||
|
||||
- `CCHeap`, a purely functional heap structure
|
||||
- `CCVector`, a growable array (pure OCaml, no C) with mutability annotations
|
||||
- `CCList`, functions on lists, including tail-recursive implementations of `map` and `append` and many other things
|
||||
- `CCArray`, utilities on arrays
|
||||
- `CCArray_slice`, array slices
|
||||
- `CCHashtbl`, `CCMap` extensions of the standard modules `Hashtbl` and `Map`
|
||||
- `CCInt`
|
||||
- `CCString` (basic string operations)
|
||||
- `CCPair` (cartesian products)
|
||||
- `CCOpt` (options, very useful)
|
||||
- `CCFun` (function combinators)
|
||||
- `CCBool`
|
||||
- `CCFloat`
|
||||
- `CCOrd` (combinators for total orderings)
|
||||
- `CCRandom` (combinators for random generators)
|
||||
- `CCHash` (hashing combinators)
|
||||
- `CCResult` (monadic error handling, very useful)
|
||||
- `CCIO`, basic utilities for IO (channels, files)
|
||||
- `CCInt64,` utils for `int64`
|
||||
- `CCChar`, utils for `char`
|
||||
- `CCFormat`, pretty-printing utils around `Format`
|
||||
|
||||
=== Containers.data
|
||||
|
||||
- `CCBitField`, bitfields embedded in integers
|
||||
- `CCCache`, memoization caches, LRU, etc.
|
||||
- `CCFlatHashtbl`, a flat (open-addressing) hashtable functorial implementation
|
||||
- `CCTrie`, a prefix tree
|
||||
- `CCHashTrie`, a map where keys are hashed and put in a trie by hash
|
||||
- `CCMultimap` and `CCMultiset`, functors defining persistent structures
|
||||
- `CCFQueue`, a purely functional double-ended queue structure
|
||||
- `CCBV`, mutable bitvectors
|
||||
- `CCHashSet`, mutable set
|
||||
- `CCPersistentHashtbl` and `CCPersistentArray`, a semi-persistent array and hashtable
|
||||
(similar to https://www.lri.fr/~filliatr/ftp/ocaml/ds/parray.ml.html[persistent arrays])
|
||||
- `CCMixmap`, `CCMixtbl`, `CCMixset`, containers of universal types (heterogenous containers)
|
||||
- `CCRingBuffer`, a double-ended queue on top of an array-like structure,
|
||||
with batch operations
|
||||
- `CCIntMap`, map specialized for integer keys based on Patricia Trees,
|
||||
with fast merges
|
||||
- `CCGraph`, a small collection of graph algorithms
|
||||
- `CCBitField`, a type-safe implementation of bitfields that fit in `int`
|
||||
- `CCWBTree`, a weight-balanced tree, implementing a map interface
|
||||
- `CCRAL`, a random-access list structure, with `O(1)` cons/hd/tl and `O(ln(n))`
|
||||
access to elements by their index.
|
||||
- `CCImmutArray`, immutable interface to arrays
|
||||
|
||||
=== Containers.unix
|
||||
|
||||
- `CCUnix`, utils for `Unix`
|
||||
|
||||
=== Containers.sexp
|
||||
|
||||
A small S-expression library.
|
||||
|
||||
- `CCSexp`, a small S-expression library
|
||||
|
||||
=== Containers.iter
|
||||
|
||||
Iterators:
|
||||
|
||||
- `CCKList`, a persistent iterator structure (akin to a lazy list, without memoization)
|
||||
- `CCKTree`, an abstract lazy tree structure
|
||||
|
||||
=== Thread
|
||||
|
||||
In the library `containers.thread`, for preemptive system threads:
|
||||
|
||||
- `CCFuture`, a set of tools for preemptive threading, including a thread pool,
|
||||
monadic futures, and MVars (concurrent boxes)
|
||||
- `CCLock`, values protected by locks
|
||||
- `CCSemaphore`, a simple implementation of semaphores
|
||||
- `CCThread` basic wrappers for `Thread`
|
||||
|
||||
=== Misc
|
||||
|
||||
The library has moved to https://github.com/c-cube/containers-misc .
|
||||
|
||||
=== Others
|
||||
|
||||
`containers.lwt` has moved to https://github.com/c-cube/containers-lwt .
|
||||
|
||||
[[build]]
|
||||
See http://c-cube.github.io/ocaml-containers/[the documentation]
|
||||
and <<tutorial,the tutorial below>> for a gentle introduction.
|
||||
|
||||
== Documentation
|
||||
|
||||
In general, see http://c-cube.github.io/ocaml-containers/ or
|
||||
http://cedeela.fr/~simon/software/containers
|
||||
http://cedeela.fr/~simon/software/containers for the **API documentation**.
|
||||
|
||||
by version:
|
||||
Some examples can be found link:doc/containers.adoc[there].
|
||||
|
||||
API documentation by version:
|
||||
|
||||
- http://c-cube.github.io/ocaml-containers/dev/[dev branch]
|
||||
- http://c-cube.github.io/ocaml-containers/0.17/[0.17]
|
||||
- http://c-cube.github.io/ocaml-containers/1.0/[1.0]
|
||||
- http://c-cube.github.io/ocaml-containers/0.19/[0.19]
|
||||
- http://c-cube.github.io/ocaml-containers/0.17/[0.17]
|
||||
|
||||
[[build]]
|
||||
== Build
|
||||
|
||||
You will need OCaml `>=` 4.00.0.
|
||||
You will need OCaml `>=` 4.01.0.
|
||||
|
||||
=== Via opam
|
||||
|
||||
|
|
@ -251,3 +144,297 @@ A few guidelines:
|
|||
- add tests if possible (using `qtest`).
|
||||
|
||||
Powered by image:http://oasis.forge.ocamlcore.org/oasis-badge.png[alt="OASIS", style="border: none;", link="http://oasis.forge.ocamlcore.org/"]
|
||||
|
||||
[[tutorial]]
|
||||
== Tutorial
|
||||
|
||||
This tutorial contains a few examples to illustrate the features and
|
||||
usage of containers. We assume containers is installed and that
|
||||
the library is loaded, e.g. with:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
#require "containers";;
|
||||
----
|
||||
|
||||
=== Basics
|
||||
|
||||
We will start with a few list helpers, then look at other parts of
|
||||
the library, including printers, maps, etc.
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
|
||||
(* quick reminder of this awesome standard operator *)
|
||||
# (|>) ;;
|
||||
- : 'a -> ('a -> 'b) -> 'b = <fun>
|
||||
|
||||
# open CCList.Infix;;
|
||||
|
||||
# let l = 1 -- 100;;
|
||||
val l : int list = [1; 2; .....]
|
||||
|
||||
# l
|
||||
|> CCList.filter_map
|
||||
(fun x-> if x mod 3=0 then Some (float x) else None)
|
||||
|> CCList.take 5 ;;
|
||||
- : float list = [3.; 6.; 9.; 12.; 15.]
|
||||
|
||||
# let l2 = l |> CCList.take_while (fun x -> x<10) ;;
|
||||
val l2 : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]
|
||||
|
||||
(* an extension of Map.Make, compatible with Map.Make(CCInt) *)
|
||||
# module IntMap = CCMap.Make(CCInt);;
|
||||
|
||||
(* conversions using the "sequence" type, fast iterators that are
|
||||
pervasively used in containers. Combinators can be found
|
||||
in the opam library "sequence". *)
|
||||
# let map =
|
||||
l2
|
||||
|> List.map (fun x -> x, string_of_int x)
|
||||
|> CCList.to_seq
|
||||
|> IntMap.of_seq;;
|
||||
val map : string CCIntMap.t = <abstr>
|
||||
|
||||
(* check the type *)
|
||||
# CCList.to_seq ;;
|
||||
- : 'a list -> 'a sequence = <fun>
|
||||
# IntMap.of_seq ;;
|
||||
- : (int * 'a) CCMap.sequence -> 'a IntMap.t = <fun>
|
||||
|
||||
(* we can print, too *)
|
||||
# Format.printf "@[<2>map =@ @[<hov>%a@]@]@."
|
||||
(IntMap.print CCFormat.int CCFormat.string_quoted)
|
||||
map;;
|
||||
map =
|
||||
[1 --> "1", 2 --> "2", 3 --> "3", 4 --> "4", 5 --> "5", 6 --> "6",
|
||||
7 --> "7", 8 --> "8", 9 --> "9"]
|
||||
- : unit = ()
|
||||
|
||||
(* options are good *)
|
||||
# IntMap.get 3 map |> CCOpt.map (fun s->s ^ s);;
|
||||
- : string option = Some "33"
|
||||
|
||||
----
|
||||
|
||||
=== New types: `CCVector`, `CCHeap`, `CCResult`
|
||||
|
||||
Containers also contains (!) a few datatypes that are not from the standard
|
||||
library but that are useful in a lot of situations:
|
||||
|
||||
CCVector::
|
||||
A resizable array, with a mutability parameter. A value of type
|
||||
`('a, CCVector.ro) CCVector.t` is an immutable vector of values of type `'a`,
|
||||
whereas a `('a, CCVector.rw) CCVector.t` is a mutable vector that
|
||||
can be modified. This way, vectors can be used in a quite functional
|
||||
way, using operations such as `map` or `flat_map`, or in a more
|
||||
imperative way.
|
||||
CCHeap::
|
||||
A priority queue (currently, leftist heaps) functorized over
|
||||
a module `sig val t val leq : t -> t -> bool` that provides a type `t`
|
||||
and a partial order `leq` on `t`.
|
||||
CCResult::
|
||||
An error type for making error handling more explicit (an error monad,
|
||||
really, if you're not afraid of the "M"-word).
|
||||
Subsumes and replaces the old `CCError`.
|
||||
It uses the new `result` type from the standard library (or from
|
||||
the retrocompatibility package on opam) and provides
|
||||
many combinators for dealing with `result`.
|
||||
|
||||
Now for a few examples:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
|
||||
(* create a new empty vector. It is mutable, for otherwise it would
|
||||
not be very useful. *)
|
||||
# CCVector.create;;
|
||||
- : unit -> ('a, CCVector.rw) CCVector.t = <fun>
|
||||
|
||||
(* init, similar to Array.init, can be used to produce a
|
||||
vector that is mutable OR immutable (see the 'mut parameter?) *)
|
||||
# CCVector.init ;;
|
||||
- : int -> (int -> 'a) -> ('a, 'mut) CCVector.t = <fun>c
|
||||
|
||||
(* use the infix (--) operator for creating a range. Notice
|
||||
that v is a vector of integer but its mutability is not
|
||||
decided yet. *)
|
||||
# let v = CCVector.(1 -- 10);;
|
||||
val v : (int, '_a) CCVector.t = <abstr>
|
||||
|
||||
# Format.printf "v = @[%a@]@." (CCVector.print CCInt.print) v;;
|
||||
v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
(* now let's mutate v *)
|
||||
# CCVector.push v 42;;
|
||||
- : unit = ()
|
||||
|
||||
(* now v is a mutable vector *)
|
||||
# v;;
|
||||
- : (int, CCVector.rw) CCVector.t = <abstr>
|
||||
|
||||
(* functional combinators! *)
|
||||
# let v2 = v
|
||||
|> CCVector.map (fun x-> x+1)
|
||||
|> CCVector.filter (fun x-> x mod 2=0)
|
||||
|> CCVector.rev ;;
|
||||
val v2 : (int, '_a) CCVector.t = <abstr>
|
||||
|
||||
# Format.printf "v2 = @[%a@]@." (CCVector.print CCInt.print) v2;;
|
||||
v2 = [10, 8, 6, 4, 2]
|
||||
|
||||
(* let's transfer to a heap *)
|
||||
# module IntHeap = CCHeap.Make(struct type t = int let leq = (<=) end);;
|
||||
|
||||
# let h = v2 |> CCVector.to_seq |> IntHeap.of_seq ;;
|
||||
val h : IntHeap.t = <abstr>
|
||||
|
||||
(* We can print the content of h
|
||||
(printing is not necessarily in order, though) *)
|
||||
# Format.printf "h = [@[%a@]]@." (IntHeap.print CCInt.print) h;;
|
||||
h = [2,4,6,8,10]
|
||||
|
||||
(* we can remove the first element, which also returns a new heap
|
||||
that does not contain it — CCHeap is a functional data structure *)
|
||||
# IntHeap.take h;;
|
||||
- : (IntHeap.t * int) option = Some (<abstr>, 2)
|
||||
|
||||
# let h', x = IntHeap.take_exn h ;;
|
||||
val h' : IntHeap.t = <abstr>
|
||||
val x : int = 2
|
||||
|
||||
(* see, 2 is removed *)
|
||||
# IntHeap.to_list h' ;;
|
||||
- : int list = [4; 6; 8; 10]
|
||||
|
||||
----
|
||||
|
||||
=== IO helpers
|
||||
|
||||
The core library contains a module called `CCIO` that provides useful
|
||||
functions for reading and writing files. It provides functions that
|
||||
make resource handling easy, following
|
||||
the pattern `with_resource : resource -> (access -> 'a) -> 'a` where
|
||||
the type `access` is a temporary handle to the resource (e.g.,
|
||||
imagine `resource` is a file name and `access` a file descriptor).
|
||||
Calling `with_resource r f` will access `r`, give the result to `f`,
|
||||
compute the result of `f` and, whether `f` succeeds or raises an
|
||||
error, it will free the resource.
|
||||
|
||||
Consider for instance:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# CCIO.with_out "/tmp/foobar"
|
||||
(fun out_channel ->
|
||||
CCIO.write_lines_l out_channel ["hello"; "world"]);;
|
||||
- : unit = ()
|
||||
----
|
||||
|
||||
This just opened the file '/tmp/foobar', creating it if it didn't exist,
|
||||
and wrote two lines in it. We did not have to close the file descriptor
|
||||
because `with_out` took care of it. By the way, the type signatures are:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
val with_out :
|
||||
?mode:int -> ?flags:open_flag list ->
|
||||
string -> (out_channel -> 'a) -> 'a
|
||||
|
||||
val write_lines_l : out_channel -> string list -> unit
|
||||
----
|
||||
|
||||
So we see the pattern for `with_out` (which opens a function in write
|
||||
mode and gives its functional argument the corresponding file descriptor).
|
||||
|
||||
NOTE: you should never let the resource escape the
|
||||
scope of the `with_resource` call, because it will not be valid outside.
|
||||
OCaml's type system doesn't make it easy to forbid that so we rely
|
||||
on convention here (it would be possible, but cumbersome, using
|
||||
a record with an explicitely quantified function type).
|
||||
|
||||
Now we can read the file again:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# let lines = CCIO.with_in "/tmp/foobar" CCIO.read_lines_l ;;
|
||||
val lines : string list = ["hello"; "world"]
|
||||
----
|
||||
|
||||
There are some other functions in `CCIO` that return _generators_
|
||||
instead of lists. The type of generators in containers
|
||||
is `type 'a gen = unit -> 'a option` (combinators can be
|
||||
found in the opam library called "gen"). A generator is to be called
|
||||
to obtain successive values, until it returns `None` (which means it
|
||||
has been exhausted). In particular, python users might recognize
|
||||
the function
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# CCIO.File.walk ;;
|
||||
- : string -> walk_item gen = <fun>;;
|
||||
----
|
||||
|
||||
where `type walk_item = [ `Dir | `File ] * string` is a path
|
||||
paired with a flag distinguishing files from directories.
|
||||
|
||||
|
||||
=== To go further: containers.data
|
||||
|
||||
There is also a sub-library called `containers.data`, with lots of
|
||||
more specialized data-structures.
|
||||
The documentation contains the API for all the modules
|
||||
(see link:README.adoc[the readme]); they also provide
|
||||
interface to `sequence` and, as the rest of containers, minimize
|
||||
dependencies over other modules. To use `containers.data` you need to link it,
|
||||
either in your build system or by `#require containers.data;;`
|
||||
|
||||
A quick example based on purely functional double-ended queues:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# #require "containers.data";;
|
||||
# #install_printer CCFQueue.print;; (* better printing of queues! *)
|
||||
|
||||
# let q = CCFQueue.of_list [2;3;4] ;;
|
||||
val q : int CCFQueue.t = queue {2; 3; 4}
|
||||
|
||||
# let q2 = q |> CCFQueue.cons 1 |> CCFQueue.cons 0 ;;
|
||||
val q2 : int CCFQueue.t = queue {0; 1; 2; 3; 4}
|
||||
|
||||
(* remove first element *)
|
||||
# CCFQueue.take_front q2;;
|
||||
- : (int * int CCFQueue.t) option = Some (0, queue {1; 2; 3; 4})
|
||||
|
||||
(* q was not changed *)
|
||||
# CCFQueue.take_front q;;
|
||||
- : (int * int CCFQueue.t) option = Some (2, queue {3; 4})
|
||||
|
||||
(* take works on both ends of the queue *)
|
||||
# CCFQueue.take_back_l 2 q2;;
|
||||
- : int CCFQueue.t * int list = (queue {0; 1; 2}, [3; 4])
|
||||
|
||||
----
|
||||
|
||||
=== Common Type Definitions
|
||||
|
||||
Some structural types are used throughout the library:
|
||||
|
||||
gen:: `'a gen = unit -> 'a option` is an iterator type. Many combinators
|
||||
are defined in the opam library https://github.com/c-cube/gen[gen]
|
||||
sequence:: `'a sequence = (unit -> 'a) -> unit` is also an iterator type.
|
||||
It is easier to define on data structures than `gen`, but it a bit less
|
||||
powerful. The opam library https://github.com/c-cube/sequence[sequence]
|
||||
can be used to consume and produce values of this type.
|
||||
error:: `'a or_error = ('a, string) result = Error of string | Ok of 'a`
|
||||
using the standard `result` type, supported in `CCResult`.
|
||||
klist:: `'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]` is a lazy list
|
||||
without memoization, used as a persistent iterator. The reference
|
||||
module is `CCKList` (in `containers.iter`).
|
||||
printer:: `'a printer = Format.formatter -> 'a -> unit` is a pretty-printer
|
||||
to be used with the standard module `Format`. In particular, in many cases,
|
||||
`"foo: %a" Foo.print foo` will type-check.
|
||||
|
||||
=== Extended Documentation
|
||||
|
||||
See link:doc/containers.adoc[the extended documentation] for more examples.
|
||||
|
|
|
|||
275
TUTORIAL.adoc
275
TUTORIAL.adoc
|
|
@ -1,275 +0,0 @@
|
|||
= Tutorial
|
||||
:source-highlighter: pygments
|
||||
|
||||
This tutorial contains a few examples to illustrate the features and
|
||||
usage of containers. We assume containers is installed and that
|
||||
the library is loaded, e.g. with:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
#require "containers";;
|
||||
----
|
||||
|
||||
== Basics
|
||||
|
||||
We will start with a few list helpers, then look at other parts of
|
||||
the library, including printers, maps, etc.
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
|
||||
(* quick reminder of this awesome standard operator *)
|
||||
# (|>) ;;
|
||||
- : 'a -> ('a -> 'b) -> 'b = <fun>
|
||||
|
||||
# open CCList.Infix;;
|
||||
|
||||
# let l = 1 -- 100;;
|
||||
val l : int list = [1; 2; .....]
|
||||
|
||||
# l
|
||||
|> CCList.filter_map
|
||||
(fun x-> if x mod 3=0 then Some (float x) else None)
|
||||
|> CCList.take 5 ;;
|
||||
- : float list = [3.; 6.; 9.; 12.; 15.]
|
||||
|
||||
# let l2 = l |> CCList.take_while (fun x -> x<10) ;;
|
||||
val l2 : int list = [1; 2; 3; 4; 5; 6; 7; 8; 9]
|
||||
|
||||
(* an extension of Map.Make, compatible with Map.Make(CCInt) *)
|
||||
# module IntMap = CCMap.Make(CCInt);;
|
||||
|
||||
(* conversions using the "sequence" type, fast iterators that are
|
||||
pervasively used in containers. Combinators can be found
|
||||
in the opam library "sequence". *)
|
||||
# let map =
|
||||
l2
|
||||
|> List.map (fun x -> x, string_of_int x)
|
||||
|> CCList.to_seq
|
||||
|> IntMap.of_seq;;
|
||||
val map : string CCIntMap.t = <abstr>
|
||||
|
||||
(* check the type *)
|
||||
# CCList.to_seq ;;
|
||||
- : 'a list -> 'a sequence = <fun>
|
||||
# IntMap.of_seq ;;
|
||||
- : (int * 'a) CCMap.sequence -> 'a IntMap.t = <fun>
|
||||
|
||||
(* we can print, too *)
|
||||
# Format.printf "@[<2>map =@ @[<hov>%a@]@]@."
|
||||
(IntMap.print CCFormat.int CCFormat.string_quoted)
|
||||
map;;
|
||||
map =
|
||||
[1 --> "1", 2 --> "2", 3 --> "3", 4 --> "4", 5 --> "5", 6 --> "6",
|
||||
7 --> "7", 8 --> "8", 9 --> "9"]
|
||||
- : unit = ()
|
||||
|
||||
(* options are good *)
|
||||
# IntMap.get 3 map |> CCOpt.map (fun s->s ^ s);;
|
||||
- : string option = Some "33"
|
||||
|
||||
----
|
||||
|
||||
== New types: `CCVector`, `CCHeap`, `CCError`, `CCResult`
|
||||
|
||||
Containers also contains (!) a few datatypes that are not from the standard
|
||||
library but that are useful in a lot of situations:
|
||||
|
||||
CCVector::
|
||||
A resizable array, with a mutability parameter. A value of type
|
||||
`('a, CCVector.ro) CCVector.t` is an immutable vector of values of type `'a`,
|
||||
whereas a `('a, CCVector.rw) CCVector.t` is a mutable vector that
|
||||
can be modified. This way, vectors can be used in a quite functional
|
||||
way, using operations such as `map` or `flat_map`, or in a more
|
||||
imperative way.
|
||||
CCHeap::
|
||||
A priority queue (currently, leftist heaps) functorized over
|
||||
a module `sig val t val leq : t -> t -> bool` that provides a type `t`
|
||||
and a partial order `leq` on `t`.
|
||||
CCError::
|
||||
An error type for making error handling more explicit (an error monad,
|
||||
really, if you're not afraid of the "M"-word). It is similar to the
|
||||
more recent `CCResult`, but works with polymorphic variants for
|
||||
compatibility with the numerous libraries that use the same type,
|
||||
that is, `type ('a, 'b) CCError.t = [`Ok of 'a | `Error of 'b]`.
|
||||
CCResult::
|
||||
It uses the new `result` type from the standard library (or from
|
||||
the retrocompatibility package on opam), and presents an interface
|
||||
similar to `CCError`. In an indeterminate amount of time, it will
|
||||
totally replace `CCError`.
|
||||
|
||||
Now for a few examples:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
|
||||
(* create a new empty vector. It is mutable, for otherwise it would
|
||||
not be very useful. *)
|
||||
# CCVector.create;;
|
||||
- : unit -> ('a, CCVector.rw) CCVector.t = <fun>
|
||||
|
||||
(* init, similar to Array.init, can be used to produce a
|
||||
vector that is mutable OR immutable (see the 'mut parameter?) *)
|
||||
# CCVector.init ;;
|
||||
- : int -> (int -> 'a) -> ('a, 'mut) CCVector.t = <fun>c
|
||||
|
||||
(* use the infix (--) operator for creating a range. Notice
|
||||
that v is a vector of integer but its mutability is not
|
||||
decided yet. *)
|
||||
# let v = CCVector.(1 -- 10);;
|
||||
val v : (int, '_a) CCVector.t = <abstr>
|
||||
|
||||
# Format.printf "v = @[%a@]@." (CCVector.print CCInt.print) v;;
|
||||
v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
|
||||
(* now let's mutate v *)
|
||||
# CCVector.push v 42;;
|
||||
- : unit = ()
|
||||
|
||||
(* now v is a mutable vector *)
|
||||
# v;;
|
||||
- : (int, CCVector.rw) CCVector.t = <abstr>
|
||||
|
||||
(* functional combinators! *)
|
||||
# let v2 = v
|
||||
|> CCVector.map (fun x-> x+1)
|
||||
|> CCVector.filter (fun x-> x mod 2=0)
|
||||
|> CCVector.rev ;;
|
||||
val v2 : (int, '_a) CCVector.t = <abstr>
|
||||
|
||||
# Format.printf "v2 = @[%a@]@." (CCVector.print CCInt.print) v2;;
|
||||
v2 = [10, 8, 6, 4, 2]
|
||||
|
||||
(* let's transfer to a heap *)
|
||||
# module IntHeap = CCHeap.Make(struct type t = int let leq = (<=) end);;
|
||||
|
||||
# let h = v2 |> CCVector.to_seq |> IntHeap.of_seq ;;
|
||||
val h : IntHeap.t = <abstr>
|
||||
|
||||
(* We can print the content of h
|
||||
(printing is not necessarily in order, though) *)
|
||||
# Format.printf "h = [@[%a@]]@." (IntHeap.print CCInt.print) h;;
|
||||
h = [2,4,6,8,10]
|
||||
|
||||
(* we can remove the first element, which also returns a new heap
|
||||
that does not contain it — CCHeap is a functional data structure *)
|
||||
# IntHeap.take h;;
|
||||
- : (IntHeap.t * int) option = Some (<abstr>, 2)
|
||||
|
||||
# let h', x = IntHeap.take_exn h ;;
|
||||
val h' : IntHeap.t = <abstr>
|
||||
val x : int = 2
|
||||
|
||||
(* see, 2 is removed *)
|
||||
# IntHeap.to_list h' ;;
|
||||
- : int list = [4; 6; 8; 10]
|
||||
|
||||
----
|
||||
|
||||
== IO helpers
|
||||
|
||||
The core library contains a module called `CCIO` that provides useful
|
||||
functions for reading and writing files. It provides functions that
|
||||
make resource handling easy, following
|
||||
the pattern `with_resource : resource -> (access -> 'a) -> 'a` where
|
||||
the type `access` is a temporary handle to the resource (e.g.,
|
||||
imagine `resource` is a file name and `access` a file descriptor).
|
||||
Calling `with_resource r f` will access `r`, give the result to `f`,
|
||||
compute the result of `f` and, whether `f` succeeds or raises an
|
||||
error, it will free the resource.
|
||||
|
||||
Consider for instance:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# CCIO.with_out "/tmp/foobar"
|
||||
(fun out_channel ->
|
||||
CCIO.write_lines_l out_channel ["hello"; "world"]);;
|
||||
- : unit = ()
|
||||
----
|
||||
|
||||
This just opened the file '/tmp/foobar', creating it if it didn't exist,
|
||||
and wrote two lines in it. We did not have to close the file descriptor
|
||||
because `with_out` took care of it. By the way, the type signatures are:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
val with_out :
|
||||
?mode:int -> ?flags:open_flag list ->
|
||||
string -> (out_channel -> 'a) -> 'a
|
||||
|
||||
val write_lines_l : out_channel -> string list -> unit
|
||||
----
|
||||
|
||||
So we see the pattern for `with_out` (which opens a function in write
|
||||
mode and gives its functional argument the corresponding file descriptor).
|
||||
|
||||
NOTE: you should never let the resource escape the
|
||||
scope of the `with_resource` call, because it will not be valid outside.
|
||||
OCaml's type system doesn't make it easy to forbid that so we rely
|
||||
on convention here (it would be possible, but cumbersome, using
|
||||
a record with an explicitely quantified function type).
|
||||
|
||||
Now we can read the file again:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# let lines = CCIO.with_in "/tmp/foobar" CCIO.read_lines_l ;;
|
||||
val lines : string list = ["hello"; "world"]
|
||||
----
|
||||
|
||||
There are some other functions in `CCIO` that return _generators_
|
||||
instead of lists. The type of generators in containers
|
||||
is `type 'a gen = unit -> 'a option` (combinators can be
|
||||
found in the opam library called "gen"). A generator is to be called
|
||||
to obtain successive values, until it returns `None` (which means it
|
||||
has been exhausted). In particular, python users might recognize
|
||||
the function
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# CCIO.File.walk ;;
|
||||
- : string -> walk_item gen = <fun>;;
|
||||
----
|
||||
|
||||
where `type walk_item = [ `Dir | `File ] * string` is a path
|
||||
paired with a flag distinguishing files from directories.
|
||||
|
||||
|
||||
== To go further: containers.data
|
||||
|
||||
There is also a sub-library called `containers.data`, with lots of
|
||||
more specialized data-structures.
|
||||
The documentation contains the API for all the modules
|
||||
(see link:README.adoc[the readme]); they also provide
|
||||
interface to `sequence` and, as the rest of containers, minimize
|
||||
dependencies over other modules. To use `containers.data` you need to link it,
|
||||
either in your build system or by `#require containers.data;;`
|
||||
|
||||
A quick example based on purely functional double-ended queues:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# #require "containers.data";;
|
||||
# #install_printer CCFQueue.print;; (* better printing of queues! *)
|
||||
|
||||
# let q = CCFQueue.of_list [2;3;4] ;;
|
||||
val q : int CCFQueue.t = queue {2; 3; 4}
|
||||
|
||||
# let q2 = q |> CCFQueue.cons 1 |> CCFQueue.cons 0 ;;
|
||||
val q2 : int CCFQueue.t = queue {0; 1; 2; 3; 4}
|
||||
|
||||
(* remove first element *)
|
||||
# CCFQueue.take_front q2;;
|
||||
- : (int * int CCFQueue.t) option = Some (0, queue {1; 2; 3; 4})
|
||||
|
||||
(* q was not changed *)
|
||||
# CCFQueue.take_front q;;
|
||||
- : (int * int CCFQueue.t) option = Some (2, queue {3; 4})
|
||||
|
||||
(* take works on both ends of the queue *)
|
||||
# CCFQueue.take_back_l 2 q2;;
|
||||
- : int CCFQueue.t * int list = (queue {0; 1; 2}, [3; 4])
|
||||
|
||||
----
|
||||
|
||||
2
_oasis
2
_oasis
|
|
@ -1,6 +1,6 @@
|
|||
OASISFormat: 0.4
|
||||
Name: containers
|
||||
Version: 1.0
|
||||
Version: 1.1
|
||||
Homepage: https://github.com/c-cube/ocaml-containers
|
||||
Authors: Simon Cruanes
|
||||
License: BSD-2-clause
|
||||
|
|
|
|||
77
doc/containers.adoc
Normal file
77
doc/containers.adoc
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
= OCaml-containers =
|
||||
:toc: macro
|
||||
:source-highlighter: pygments
|
||||
|
||||
This document contains more information on some modules of Containers.
|
||||
|
||||
toc::[]
|
||||
|
||||
== Hash combinators: `CCHash`
|
||||
|
||||
Although OCaml provides polymorphic hash tables (`('a,'b) Hashtbl.t`)
|
||||
using the polymorphic equality `(=)` and hash `Hashtbl.hash`, it is often
|
||||
safer and more efficient to use `Hashtbl.Make` (or the extended `CCHashtbl.Make`)
|
||||
with custom equality and hash functions.
|
||||
|
||||
`CCHash` provides combinators for writing hash functions:
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
# module H = CCHash;;
|
||||
|
||||
# let hash1 : (int * bool) list H.t = H.(list (pair int bool));;
|
||||
|
||||
# hash1 [1, true; 2, false; 3, true];;
|
||||
- : int = 636041136
|
||||
|
||||
(* the function hashes the whole value, can be costly *)
|
||||
# hash1 CCList.(1 -- 1000 |> map (fun i->i, i mod 2 = 0));;
|
||||
- : int = 845685523
|
||||
# hash1 CCList.(1 -- 1001 |> map (fun i->i, i mod 2 = 0));;
|
||||
- : int = 381026697
|
||||
----
|
||||
|
||||
The polymorphic hash function is still present, as `CCHash.poly`.
|
||||
The functions `CCHash.list_comm` and `CCHash.array_comm` allow to hash
|
||||
lists and arrays while ignoring the order of elements: all permutations
|
||||
of the input will have the same hash.
|
||||
|
||||
|
||||
|
||||
== Parser Combinator: `CCParse`
|
||||
:toc: macro
|
||||
:source-highlighter: pygments
|
||||
|
||||
The module `CCParse` defines basic parser combinators on strings.
|
||||
Adapting https://github.com/inhabitedtype/angstrom#usage[angstrom's tutorial example] gives the following snippet.
|
||||
Note that backtracking is explicit in `CCParse`, hence
|
||||
the use of `try_` to allow it in some places.
|
||||
Explicit memoization with `memo` and `fix_memo` is also possible.
|
||||
|
||||
[source,OCaml]
|
||||
----
|
||||
open CCParse.Infix;;
|
||||
module P = CCParse;;
|
||||
|
||||
let parens p = P.try_ (P.char '(') *> p <* P.char ')' ;;
|
||||
let add = P.char '+' *> P.return (+) ;;
|
||||
let sub = P.char '-' *> P.return (-) ;;
|
||||
let mul = P.char '*' *> P.return ( * ) ;;
|
||||
let div = P.char '/' *> P.return ( / ) ;;
|
||||
let integer =
|
||||
P.chars1_if (function '0'..'9'->true|_->false) >|= int_of_string ;;
|
||||
|
||||
let chainl1 e op =
|
||||
P.fix (fun r ->
|
||||
e >>= fun x -> P.try_ (op <*> P.return x <*> r) <|> P.return x) ;;
|
||||
|
||||
let expr : int P.t =
|
||||
P.fix (fun expr ->
|
||||
let factor = parens expr <|> integer in
|
||||
let term = chainl1 factor (mul <|> div) in
|
||||
chainl1 term (add <|> sub)) ;;
|
||||
|
||||
P.parse_string expr "4*1+2";; (* Ok 6 *)
|
||||
P.parse_string expr "4*(1+2)";; (* Ok 12 *)
|
||||
|
||||
----
|
||||
BIN
media/logo.png
BIN
media/logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 89 KiB |
|
|
@ -26,15 +26,7 @@ end
|
|||
| [] -> true
|
||||
| x::((y::_) as l') -> x <= y && is_sorted l'
|
||||
|
||||
let extract_list heap =
|
||||
let rec recurse acc h =
|
||||
if H.is_empty h
|
||||
then List.rev acc
|
||||
else
|
||||
let h', x = H.take_exn h in
|
||||
recurse (x::acc) h'
|
||||
in
|
||||
recurse [] heap
|
||||
let extract_list = H.to_list_sorted
|
||||
*)
|
||||
|
||||
(*$R
|
||||
|
|
@ -77,6 +69,15 @@ end
|
|||
)
|
||||
*)
|
||||
|
||||
(*$QR
|
||||
Q.(list_of_size Gen.(return 1_000) int) (fun l ->
|
||||
(* put elements into a heap *)
|
||||
let h = H.of_seq (Sequence.of_list l) in
|
||||
let l' = H.to_seq_sorted h |> Sequence.to_list in
|
||||
is_sorted l'
|
||||
)
|
||||
*)
|
||||
|
||||
module type S = sig
|
||||
type elt
|
||||
type t
|
||||
|
|
@ -133,17 +134,31 @@ module type S = sig
|
|||
are now [add_seq], [add_gen], [add_klist]) *)
|
||||
|
||||
val to_list : t -> elt list
|
||||
(** Return the elements of the heap, in no particular order. *)
|
||||
|
||||
val add_list : t -> elt list -> t (** @since 0.16 *)
|
||||
val to_list_sorted : t -> elt list
|
||||
(** Return the elements in increasing order
|
||||
@since 1.1 *)
|
||||
|
||||
val add_list : t -> elt list -> t
|
||||
(** Add the elements of the list to the heap. An element occurring several
|
||||
times will be added that many times to the heap.
|
||||
@since 0.16 *)
|
||||
|
||||
val of_list : elt list -> t
|
||||
(** [of_list l = add_list empty l] *)
|
||||
|
||||
val add_seq : t -> elt sequence -> t (** @since 0.16 *)
|
||||
(** Similar to {!add_list} *)
|
||||
|
||||
val of_seq : elt sequence -> t
|
||||
|
||||
val to_seq : t -> elt sequence
|
||||
|
||||
val to_seq_sorted : t -> elt sequence
|
||||
(** Iterate on the elements, in increasing order
|
||||
@since 1.1 *)
|
||||
|
||||
val add_klist : t -> elt klist -> t (** @since 0.16 *)
|
||||
|
||||
val of_klist : elt klist -> t
|
||||
|
|
@ -251,6 +266,13 @@ module Make(E : PARTIAL_ORD) : S with type elt = E.t = struct
|
|||
x::aux (aux acc l) r
|
||||
in aux [] h
|
||||
|
||||
let to_list_sorted heap =
|
||||
let rec recurse acc h = match take h with
|
||||
| None -> List.rev acc
|
||||
| Some (h',x) -> recurse (x::acc) h'
|
||||
in
|
||||
recurse [] heap
|
||||
|
||||
let add_list h l = List.fold_left add h l
|
||||
|
||||
let of_list l = add_list empty l
|
||||
|
|
@ -264,6 +286,13 @@ module Make(E : PARTIAL_ORD) : S with type elt = E.t = struct
|
|||
|
||||
let to_seq h k = iter k h
|
||||
|
||||
let to_seq_sorted heap =
|
||||
let rec recurse h k = match take h with
|
||||
| None -> ()
|
||||
| Some (h',x) -> k x; recurse h' k
|
||||
in
|
||||
fun k -> recurse heap k
|
||||
|
||||
let rec add_klist h l = match l() with
|
||||
| `Nil -> h
|
||||
| `Cons (x, l') ->
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ module type S = sig
|
|||
are now [add_seq], [add_gen], [add_klist]) *)
|
||||
|
||||
val to_list : t -> elt list
|
||||
(** Return the elements of the heap, in no particular order. *)
|
||||
|
||||
val to_list_sorted : t -> elt list
|
||||
(** Return the elements in increasing order
|
||||
@since 1.1 *)
|
||||
|
||||
val add_list : t -> elt list -> t
|
||||
(** Add the elements of the list to the heap. An element occurring several
|
||||
|
|
@ -87,6 +92,10 @@ module type S = sig
|
|||
|
||||
val to_seq : t -> elt sequence
|
||||
|
||||
val to_seq_sorted : t -> elt sequence
|
||||
(** Iterate on the elements, in increasing order
|
||||
@since 1.1 *)
|
||||
|
||||
val add_klist : t -> elt klist -> t (** @since 0.16 *)
|
||||
|
||||
val of_klist : elt klist -> t
|
||||
|
|
|
|||
|
|
@ -320,6 +320,14 @@ module File = struct
|
|||
)
|
||||
*)
|
||||
|
||||
let walk_l d =
|
||||
let l = ref [] in
|
||||
let g = walk d in
|
||||
let rec aux () = match g() with
|
||||
| None -> !l
|
||||
| Some x -> l := x :: !l; aux ()
|
||||
in aux ()
|
||||
|
||||
type walk_item = [`File | `Dir] * t
|
||||
|
||||
let read_dir ?(recurse=false) d =
|
||||
|
|
|
|||
|
|
@ -202,6 +202,11 @@ module File : sig
|
|||
symlinks, etc.)
|
||||
@raise Sys_error in case of error (e.g. permission denied) during iteration *)
|
||||
|
||||
val walk_l : t -> walk_item list
|
||||
(** Same as {!walk} but returns a list (therefore it's eager and might
|
||||
take some time on large directories)
|
||||
@since 1.1 *)
|
||||
|
||||
val show_walk_item : walk_item -> string
|
||||
|
||||
val with_temp :
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ let map_or ~default f = function
|
|||
| None -> default
|
||||
| Some x -> f x
|
||||
|
||||
let maybe f default = map_or ~default f
|
||||
|
||||
let is_some = function
|
||||
| None -> false
|
||||
| Some _ -> true
|
||||
|
|
@ -69,7 +67,13 @@ let map2 f o1 o2 = match o1, o2 with
|
|||
|
||||
let filter p = function
|
||||
| Some x as o when p x -> o
|
||||
| o -> o
|
||||
| _ -> None
|
||||
|
||||
(*$=
|
||||
None (filter ((=) 0) (Some 1))
|
||||
(Some 0) (filter ((=) 0) (Some 0))
|
||||
None (filter (fun _ -> true) None)
|
||||
*)
|
||||
|
||||
let if_ p x = if p x then Some x else None
|
||||
|
||||
|
|
@ -89,10 +93,6 @@ let fold f acc o = match o with
|
|||
| None -> acc
|
||||
| Some x -> f acc x
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ val float : float t
|
|||
(** {2 Lexicographic Combination} *)
|
||||
|
||||
val (<?>) : int -> ('a t * 'a * 'a) -> int
|
||||
(** [c1 @@? (ord, x, y)] returns the same as [c1] if [c1] is not [0];
|
||||
(** [c1 <?> (ord, x, y)] returns the same as [c1] if [c1] is not [0];
|
||||
otherwise it uses [ord] to compare the two values [x] and [y],
|
||||
of type ['a].
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,34 @@
|
|||
|
||||
*)
|
||||
|
||||
(*$R
|
||||
let open CCParse.Infix in
|
||||
let module P = CCParse in
|
||||
|
||||
let parens p = P.try_ (P.char '(') *> p <* P.char ')' in
|
||||
let add = P.char '+' *> P.return (+) in
|
||||
let sub = P.char '-' *> P.return (-) in
|
||||
let mul = P.char '*' *> P.return ( * ) in
|
||||
let div = P.char '/' *> P.return ( / ) in
|
||||
let integer =
|
||||
P.chars1_if (function '0'..'9'->true|_->false) >|= int_of_string in
|
||||
|
||||
let chainl1 e op =
|
||||
P.fix (fun r ->
|
||||
e >>= fun x -> P.try_ (op <*> P.return x <*> r) <|> P.return x) in
|
||||
|
||||
let expr : int P.t =
|
||||
P.fix (fun expr ->
|
||||
let factor = parens expr <|> integer in
|
||||
let term = chainl1 factor (mul <|> div) in
|
||||
chainl1 term (add <|> sub)) in
|
||||
|
||||
assert_equal (Ok 6) (P.parse_string expr "4*1+2");
|
||||
assert_equal (Ok 12) (P.parse_string expr "4*(1+2)");
|
||||
()
|
||||
*)
|
||||
|
||||
|
||||
type 'a or_error = ('a, string) Result.result
|
||||
|
||||
type line_num = int
|
||||
|
|
|
|||
|
|
@ -252,16 +252,16 @@ module Traverse = struct
|
|||
bag.push (`Enter (v, []));
|
||||
while not (bag.is_empty ()) do
|
||||
match bag.pop () with
|
||||
| `Enter (x, path) ->
|
||||
if not (tags.get_tag x) then (
|
||||
| `Enter (v, path) ->
|
||||
if not (tags.get_tag v) then (
|
||||
let num = !n in
|
||||
incr n;
|
||||
tags.set_tag x;
|
||||
k (`Enter (x, num, path));
|
||||
bag.push (`Exit x);
|
||||
tags.set_tag v;
|
||||
k (`Enter (v, num, path));
|
||||
bag.push (`Exit v);
|
||||
Seq.iter
|
||||
(fun (e,v') -> bag.push (`Edge (v,e,v',(v,e,v') :: path)))
|
||||
(graph x);
|
||||
(graph v);
|
||||
)
|
||||
| `Exit x -> k (`Exit x)
|
||||
| `Edge (v,e,v', path) ->
|
||||
|
|
@ -286,6 +286,21 @@ module Traverse = struct
|
|||
} in
|
||||
dfs_tag ?eq ~tags ~graph seq
|
||||
end
|
||||
|
||||
(*$R
|
||||
let l =
|
||||
Traverse.Event.dfs ~graph:divisors_graph (Sequence.return 345614)
|
||||
|> Sequence.to_list in
|
||||
let expected =
|
||||
[`Enter (345614, 0, []); `Edge (345614, (), 172807, `Forward);
|
||||
`Enter (172807, 1, [(345614, (), 172807)]); `Edge (172807, (), 1, `Forward);
|
||||
`Enter (1, 2, [(172807, (), 1); (345614, (), 172807)]); `Exit 1; `Exit 172807;
|
||||
`Edge (345614, (), 2, `Forward); `Enter (2, 3, [(345614, (), 2)]);
|
||||
`Edge (2, (), 1, `Cross); `Exit 2; `Edge (345614, (), 1, `Cross);
|
||||
`Exit 345614]
|
||||
in
|
||||
assert_equal expected l
|
||||
*)
|
||||
end
|
||||
|
||||
(** {2 Cycles} *)
|
||||
|
|
|
|||
|
|
@ -498,6 +498,19 @@ module Make(P : PARAM) = struct
|
|||
OUnit.assert_raises Exit (fun () -> Fut.get l')
|
||||
*)
|
||||
|
||||
(*$R
|
||||
let rec fib x = if x<2 then 1 else fib (x-1)+fib(x-2) in
|
||||
let l =
|
||||
CCList.(1--10_000)
|
||||
|> List.rev_map
|
||||
(fun x-> Fut.make (fun () -> Thread.yield(); fib (x mod 30)))
|
||||
|> Fut.(map_l (fun x->x>|= fun x->x+1))
|
||||
in
|
||||
OUnit.assert_bool "not done" (Fut.state l = Waiting);
|
||||
let l' = Fut.get l in
|
||||
OUnit.assert_equal 10_000 (List.length l');
|
||||
*)
|
||||
|
||||
let choose_
|
||||
: type a. a t array_or_list -> a t
|
||||
= fun aol ->
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue