mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
random generators in several modules, and CCRandom to bind them all with fuel
This commit is contained in:
parent
6872591708
commit
9da54f3e5a
12 changed files with 399 additions and 6 deletions
3
_oasis
3
_oasis
|
|
@ -41,7 +41,8 @@ Library "containers"
|
||||||
Modules: CCVector, CCDeque, CCGen, CCSequence, CCFQueue, CCMultiMap,
|
Modules: CCVector, CCDeque, CCGen, CCSequence, CCFQueue, CCMultiMap,
|
||||||
CCMultiSet, CCBV, CCPrint, CCPersistentHashtbl, CCError,
|
CCMultiSet, CCBV, CCPrint, CCPersistentHashtbl, CCError,
|
||||||
CCLeftistheap, CCList, CCOpt, CCPair, CCFun, CCHash,
|
CCLeftistheap, CCList, CCOpt, CCPair, CCFun, CCHash,
|
||||||
CCKList, CCInt, CCBool, CCArray, CCBatch, CCOrd, CCLinq
|
CCKList, CCInt, CCBool, CCArray, CCBatch, CCOrd,
|
||||||
|
CCRandom, CCLinq
|
||||||
FindlibName: containers
|
FindlibName: containers
|
||||||
|
|
||||||
Library "containers_string"
|
Library "containers_string"
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
||||||
type 'a gen = unit -> 'a option
|
type 'a gen = unit -> 'a option
|
||||||
type 'a equal = 'a -> 'a -> bool
|
type 'a equal = 'a -> 'a -> bool
|
||||||
type 'a ord = 'a -> 'a -> int
|
type 'a ord = 'a -> 'a -> int
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
module type S = sig
|
module type S = sig
|
||||||
type 'a t
|
type 'a t
|
||||||
|
|
@ -81,6 +82,10 @@ module type S = sig
|
||||||
val shuffle_with : Random.State.t -> 'a t -> unit
|
val shuffle_with : Random.State.t -> 'a t -> unit
|
||||||
(** Like shuffle but using a specialized random state *)
|
(** Like shuffle but using a specialized random state *)
|
||||||
|
|
||||||
|
val random_choose : 'a t -> 'a random_gen
|
||||||
|
(** Choose an element randomly.
|
||||||
|
@raise Not_found if the array/slice is empty *)
|
||||||
|
|
||||||
val to_seq : 'a t -> 'a sequence
|
val to_seq : 'a t -> 'a sequence
|
||||||
val to_gen : 'a t -> 'a gen
|
val to_gen : 'a t -> 'a gen
|
||||||
val to_klist : 'a t -> 'a klist
|
val to_klist : 'a t -> 'a klist
|
||||||
|
|
@ -161,6 +166,10 @@ let _shuffle _rand_int a i j =
|
||||||
a.(l) <- tmp;
|
a.(l) <- tmp;
|
||||||
done
|
done
|
||||||
|
|
||||||
|
let _choose a i j st =
|
||||||
|
if i>=j then raise Not_found;
|
||||||
|
a.(i+Random.int (j-i))
|
||||||
|
|
||||||
let _pp ~sep pp_item buf a i j =
|
let _pp ~sep pp_item buf a i j =
|
||||||
for k = i to j - 1 do
|
for k = i to j - 1 do
|
||||||
if k > i then Buffer.add_string buf sep;
|
if k > i then Buffer.add_string buf sep;
|
||||||
|
|
@ -321,6 +330,19 @@ let shuffle a = _shuffle Random.int a 0 (Array.length a)
|
||||||
|
|
||||||
let shuffle_with st a = _shuffle (Random.State.int st) a 0 (Array.length a)
|
let shuffle_with st a = _shuffle (Random.State.int st) a 0 (Array.length a)
|
||||||
|
|
||||||
|
let random_choose a st = _choose a 0 (Array.length a) st
|
||||||
|
|
||||||
|
let random_len n g st =
|
||||||
|
Array.init n (fun _ -> g st)
|
||||||
|
|
||||||
|
let random g st =
|
||||||
|
let n = Random.State.int st 1_000 in
|
||||||
|
random_len n g st
|
||||||
|
|
||||||
|
let random_non_empty g st =
|
||||||
|
let n = 1 + Random.State.int st 1_000 in
|
||||||
|
random_len n g st
|
||||||
|
|
||||||
let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a 0 (Array.length a)
|
let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a 0 (Array.length a)
|
||||||
|
|
||||||
let pp_i ?(sep=", ") pp_item buf a = _pp_i ~sep pp_item buf a 0 (Array.length a)
|
let pp_i ?(sep=", ") pp_item buf a = _pp_i ~sep pp_item buf a 0 (Array.length a)
|
||||||
|
|
@ -412,6 +434,8 @@ module Sub = struct
|
||||||
let shuffle_with st a =
|
let shuffle_with st a =
|
||||||
_shuffle (Random.State.int st) a.arr a.i a.j
|
_shuffle (Random.State.int st) a.arr a.i a.j
|
||||||
|
|
||||||
|
let random_choose a st = _choose a.arr a.i a.j st
|
||||||
|
|
||||||
let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a.arr a.i a.j
|
let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a.arr a.i a.j
|
||||||
|
|
||||||
let pp_i ?(sep=", ") pp_item buf a = _pp_i ~sep pp_item buf a.arr a.i a.j
|
let pp_i ?(sep=", ") pp_item buf a = _pp_i ~sep pp_item buf a.arr a.i a.j
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
||||||
type 'a gen = unit -> 'a option
|
type 'a gen = unit -> 'a option
|
||||||
type 'a equal = 'a -> 'a -> bool
|
type 'a equal = 'a -> 'a -> bool
|
||||||
type 'a ord = 'a -> 'a -> int
|
type 'a ord = 'a -> 'a -> int
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
(** {2 Abstract Signature} *)
|
(** {2 Abstract Signature} *)
|
||||||
|
|
||||||
|
|
@ -83,6 +84,10 @@ module type S = sig
|
||||||
val shuffle_with : Random.State.t -> 'a t -> unit
|
val shuffle_with : Random.State.t -> 'a t -> unit
|
||||||
(** Like shuffle but using a specialized random state *)
|
(** Like shuffle but using a specialized random state *)
|
||||||
|
|
||||||
|
val random_choose : 'a t -> 'a random_gen
|
||||||
|
(** Choose an element randomly.
|
||||||
|
@raise Not_found if the array/slice is empty *)
|
||||||
|
|
||||||
val to_seq : 'a t -> 'a sequence
|
val to_seq : 'a t -> 'a sequence
|
||||||
val to_gen : 'a t -> 'a gen
|
val to_gen : 'a t -> 'a gen
|
||||||
val to_klist : 'a t -> 'a klist
|
val to_klist : 'a t -> 'a klist
|
||||||
|
|
@ -129,6 +134,10 @@ val except_idx : 'a t -> int -> 'a list
|
||||||
val (--) : int -> int -> int t
|
val (--) : int -> int -> int t
|
||||||
(** Range array *)
|
(** Range array *)
|
||||||
|
|
||||||
|
val random : 'a random_gen -> 'a t random_gen
|
||||||
|
val random_non_empty : 'a random_gen -> 'a t random_gen
|
||||||
|
val random_len : int -> 'a random_gen -> 'a t random_gen
|
||||||
|
|
||||||
(** {2 Slices}
|
(** {2 Slices}
|
||||||
A slice is a part of an array, that requires no copying and shares
|
A slice is a part of an array, that requires no copying and shares
|
||||||
its storage with the original array.
|
its storage with the original array.
|
||||||
|
|
@ -155,7 +164,7 @@ module Sub : sig
|
||||||
|
|
||||||
val underlying : 'a t -> 'a array
|
val underlying : 'a t -> 'a array
|
||||||
(** Underlying array (shared). Modifying this array will modify the slice *)
|
(** Underlying array (shared). Modifying this array will modify the slice *)
|
||||||
|
|
||||||
val copy : 'a t -> 'a array
|
val copy : 'a t -> 'a array
|
||||||
(** Copy into a new array *)
|
(** Copy into a new array *)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
(** {1 Hash combinators}
|
(** {1 Hash combinators}
|
||||||
|
|
||||||
Combination of hashes based on the Murmur Hash (64 bits). See
|
Combination of hashes based on the Murmur Hash (64 bits). See
|
||||||
{{:https://sites.google.com/site/murmurhash/MurmurHash2_64.cpp?attredirects=0} this page}
|
{{: https://sites.google.com/site/murmurhash/MurmurHash2_64.cpp?attredirects=0} this page}
|
||||||
*)
|
*)
|
||||||
|
|
||||||
(** {2 Definitions} *)
|
(** {2 Definitions} *)
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@ let sign i =
|
||||||
|
|
||||||
type 'a printer = Buffer.t -> 'a -> unit
|
type 'a printer = Buffer.t -> 'a -> unit
|
||||||
type 'a formatter = Format.formatter -> 'a -> unit
|
type 'a formatter = Format.formatter -> 'a -> unit
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
|
let random n st = Random.State.int st n
|
||||||
|
let random_small = random 100
|
||||||
|
let random_range i j st = i + random (j-i) st
|
||||||
|
|
||||||
let pp buf = Printf.bprintf buf "%d"
|
let pp buf = Printf.bprintf buf "%d"
|
||||||
let print fmt = Format.fprintf fmt "%d"
|
let print fmt = Format.fprintf fmt "%d"
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@ val sign : t -> int
|
||||||
|
|
||||||
type 'a printer = Buffer.t -> 'a -> unit
|
type 'a printer = Buffer.t -> 'a -> unit
|
||||||
type 'a formatter = Format.formatter -> 'a -> unit
|
type 'a formatter = Format.formatter -> 'a -> unit
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
|
val random : int -> t random_gen
|
||||||
|
val random_small : t random_gen
|
||||||
|
val random_range : int -> int -> t random_gen
|
||||||
|
|
||||||
val pp : t printer
|
val pp : t printer
|
||||||
val print : t formatter
|
val print : t formatter
|
||||||
|
|
|
||||||
|
|
@ -378,11 +378,22 @@ let range i j =
|
||||||
range 5 2 = [5;4;3;2]
|
range 5 2 = [5;4;3;2]
|
||||||
*)
|
*)
|
||||||
|
|
||||||
|
let range' i j =
|
||||||
|
if i<j then range i (j-1)
|
||||||
|
else if i=j then []
|
||||||
|
else range i (j+1)
|
||||||
|
|
||||||
|
(*$T
|
||||||
|
range' 0 0 = []
|
||||||
|
range' 0 5 = [0;1;2;3;4]
|
||||||
|
range' 5 2 = [5;4;3]
|
||||||
|
*)
|
||||||
|
|
||||||
let (--) = range
|
let (--) = range
|
||||||
|
|
||||||
(*$T
|
(*$T
|
||||||
append (range 0 100) (range 101 1000) = range 0 1000
|
append (range 0 100) (range 101 1000) = range 0 1000
|
||||||
append (range 1000 500) (range 499 0) = range 1000 0
|
append (range 1000 501) (range 500 0) = range 1000 0
|
||||||
*)
|
*)
|
||||||
|
|
||||||
let replicate i x =
|
let replicate i x =
|
||||||
|
|
@ -474,7 +485,7 @@ module Zipper = struct
|
||||||
| None -> l,r
|
| None -> l,r
|
||||||
| Some x' -> l, x::r
|
| Some x' -> l, x::r
|
||||||
end
|
end
|
||||||
|
|
||||||
let focused = function
|
let focused = function
|
||||||
| _, x::_ -> Some x
|
| _, x::_ -> Some x
|
||||||
| _, [] -> None
|
| _, [] -> None
|
||||||
|
|
@ -491,6 +502,30 @@ type 'a gen = unit -> 'a option
|
||||||
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
||||||
type 'a printer = Buffer.t -> 'a -> unit
|
type 'a printer = Buffer.t -> 'a -> unit
|
||||||
type 'a formatter = Format.formatter -> 'a -> unit
|
type 'a formatter = Format.formatter -> 'a -> unit
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
|
let random_len len g st =
|
||||||
|
map (fun _ -> g st) (range' 0 len)
|
||||||
|
|
||||||
|
(*$T
|
||||||
|
random_len 10 CCInt.random_small (Random.State.make [||]) |> List.length = 10
|
||||||
|
*)
|
||||||
|
|
||||||
|
let random g st =
|
||||||
|
let len = Random.State.int st 1_000 in
|
||||||
|
random_len len g st
|
||||||
|
|
||||||
|
let random_non_empty g st =
|
||||||
|
let len = 1 + Random.State.int st 1_000 in
|
||||||
|
random_len len g st
|
||||||
|
|
||||||
|
let random_choose l = match l with
|
||||||
|
| [] -> raise Not_found
|
||||||
|
| _::_ ->
|
||||||
|
let len = List.length l in
|
||||||
|
fun st ->
|
||||||
|
let i = Random.State.int st len in
|
||||||
|
List.nth l i
|
||||||
|
|
||||||
let to_seq l k = List.iter k l
|
let to_seq l k = List.iter k l
|
||||||
let of_seq seq =
|
let of_seq seq =
|
||||||
|
|
|
||||||
|
|
@ -152,9 +152,13 @@ end
|
||||||
(** {2 Other Constructors} *)
|
(** {2 Other Constructors} *)
|
||||||
|
|
||||||
val range : int -> int -> int t
|
val range : int -> int -> int t
|
||||||
(** [range i j] iterates on integers from [i] to [j] included. It works
|
(** [range i j] iterates on integers from [i] to [j] included . It works
|
||||||
both for decreasing and increasing ranges *)
|
both for decreasing and increasing ranges *)
|
||||||
|
|
||||||
|
val range' : int -> int -> int t
|
||||||
|
(** Same as {!range} but the second bound is excluded.
|
||||||
|
For instance [range' 0 5 = [0;1;2;3;4]] *)
|
||||||
|
|
||||||
val (--) : int -> int -> int t
|
val (--) : int -> int -> int t
|
||||||
(** Infix alias for [range] *)
|
(** Infix alias for [range] *)
|
||||||
|
|
||||||
|
|
@ -223,6 +227,15 @@ type 'a gen = unit -> 'a option
|
||||||
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
||||||
type 'a printer = Buffer.t -> 'a -> unit
|
type 'a printer = Buffer.t -> 'a -> unit
|
||||||
type 'a formatter = Format.formatter -> 'a -> unit
|
type 'a formatter = Format.formatter -> 'a -> unit
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
|
val random : 'a random_gen -> 'a list random_gen
|
||||||
|
val random_non_empty : 'a random_gen -> 'a list random_gen
|
||||||
|
val random_len : int -> 'a random_gen -> 'a list random_gen
|
||||||
|
|
||||||
|
val random_choose : 'a list -> 'a random_gen
|
||||||
|
(** Randomly choose an element in the list.
|
||||||
|
@raise Not_found if the list is empty *)
|
||||||
|
|
||||||
val to_seq : 'a t -> 'a sequence
|
val to_seq : 'a t -> 'a sequence
|
||||||
val of_seq : 'a sequence -> 'a t
|
val of_seq : 'a sequence -> 'a t
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,10 @@ let of_list = function
|
||||||
type 'a sequence = ('a -> unit) -> unit
|
type 'a sequence = ('a -> unit) -> unit
|
||||||
type 'a gen = unit -> 'a option
|
type 'a gen = unit -> 'a option
|
||||||
type 'a printer = Buffer.t -> 'a -> unit
|
type 'a printer = Buffer.t -> 'a -> unit
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
|
let random g st =
|
||||||
|
if Random.State.bool st then Some (g st) else None
|
||||||
|
|
||||||
let to_gen o =
|
let to_gen o =
|
||||||
match o with
|
match o with
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,9 @@ val of_list : 'a list -> 'a t
|
||||||
type 'a sequence = ('a -> unit) -> unit
|
type 'a sequence = ('a -> unit) -> unit
|
||||||
type 'a gen = unit -> 'a option
|
type 'a gen = unit -> 'a option
|
||||||
type 'a printer = Buffer.t -> 'a -> unit
|
type 'a printer = Buffer.t -> 'a -> unit
|
||||||
|
type 'a random_gen = Random.State.t -> 'a
|
||||||
|
|
||||||
|
val random : 'a random_gen -> 'a t random_gen
|
||||||
|
|
||||||
val to_gen : 'a t -> 'a gen
|
val to_gen : 'a t -> 'a gen
|
||||||
val to_seq : 'a t -> 'a sequence
|
val to_seq : 'a t -> 'a sequence
|
||||||
|
|
|
||||||
152
core/CCRandom.ml
Normal file
152
core/CCRandom.ml
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
|
||||||
|
(*
|
||||||
|
copyright (c) 2013-2014, simon cruanes
|
||||||
|
all rights reserved.
|
||||||
|
|
||||||
|
redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer. redistributions in binary
|
||||||
|
form must reproduce the above copyright notice, this list of conditions and the
|
||||||
|
following disclaimer in the documentation and/or other materials provided with
|
||||||
|
the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
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 Random Generators} *)
|
||||||
|
|
||||||
|
type state = Random.State.t
|
||||||
|
|
||||||
|
type 'a t = state -> 'a
|
||||||
|
type 'a random_gen = 'a t
|
||||||
|
|
||||||
|
let return x _st = x
|
||||||
|
|
||||||
|
let flat_map f g st = f (g st) st
|
||||||
|
|
||||||
|
let (>>=) g f st = flat_map f g st
|
||||||
|
|
||||||
|
let map f g st = f (g st)
|
||||||
|
|
||||||
|
let (>|=) g f st = map f g st
|
||||||
|
|
||||||
|
let choose_array a st =
|
||||||
|
if Array.length a = 0 then invalid_arg "CCRandom.choose_array";
|
||||||
|
a.(Random.State.int st (Array.length a)) st
|
||||||
|
|
||||||
|
let choose l = choose_array (Array.of_list l)
|
||||||
|
|
||||||
|
let choose_return l = choose_array (Array.of_list (List.map return l))
|
||||||
|
|
||||||
|
(** {2 Fuel and Backtracking} *)
|
||||||
|
|
||||||
|
module Fuel = struct
|
||||||
|
type fuel = int
|
||||||
|
|
||||||
|
exception Backtrack
|
||||||
|
|
||||||
|
(* consume [d] units of fuel and return [x] if it works *)
|
||||||
|
let _consume d fuel x =
|
||||||
|
if fuel >= d then Some (fuel-d,x) else None
|
||||||
|
|
||||||
|
let _split i st =
|
||||||
|
if i < 2 then raise Backtrack
|
||||||
|
else
|
||||||
|
let j = 1 + Random.State.int st (i-1) in
|
||||||
|
(j, i-j)
|
||||||
|
|
||||||
|
let split i st = try Some (_split i st) with Backtrack -> None
|
||||||
|
|
||||||
|
(* partition of an int into [len] integers. We divide-and-conquer on
|
||||||
|
the expected length, until it reaches 1. *)
|
||||||
|
let split_list i ~len st =
|
||||||
|
let rec aux i ~len acc =
|
||||||
|
if i < len then raise Backtrack
|
||||||
|
else if len = 1 then i::acc
|
||||||
|
else
|
||||||
|
(* split somewhere in the middle *)
|
||||||
|
let len1, len2 = _split len st in
|
||||||
|
if i = len
|
||||||
|
then aux len1 ~len:len1 (aux len2 ~len:len2 acc)
|
||||||
|
else
|
||||||
|
let i1, i2 = _split (i-len1-len2) st in
|
||||||
|
aux i1 ~len:len1 (aux i2 ~len:len2 acc)
|
||||||
|
in
|
||||||
|
try Some (aux i ~len []) with Backtrack -> None
|
||||||
|
|
||||||
|
(** {6 Fueled Generators} *)
|
||||||
|
|
||||||
|
type 'a t = fuel -> state -> (fuel * 'a) option
|
||||||
|
|
||||||
|
let return x fuel _st = _consume 1 fuel x
|
||||||
|
|
||||||
|
let return' fuel x fuel' _st = _consume fuel fuel' x
|
||||||
|
|
||||||
|
let flat_map f g fuel st =
|
||||||
|
match g fuel st with
|
||||||
|
| None -> None
|
||||||
|
| Some (fuel, x) -> f x fuel st
|
||||||
|
|
||||||
|
let (>>=) g f = flat_map f g
|
||||||
|
|
||||||
|
let map f g fuel st =
|
||||||
|
match g fuel st with
|
||||||
|
| None -> None
|
||||||
|
| Some (fuel, x) -> Some (fuel, f x)
|
||||||
|
|
||||||
|
let (>|=) g f = map f g
|
||||||
|
|
||||||
|
let consume fuel _st = _consume 1 fuel ()
|
||||||
|
|
||||||
|
let consume' fuel fuel' _st = _consume fuel fuel' ()
|
||||||
|
|
||||||
|
let fail _fuel _st = None
|
||||||
|
|
||||||
|
let retry ?(max=10) g fuel st =
|
||||||
|
let rec aux n =
|
||||||
|
match g fuel st with
|
||||||
|
| None when n=0 -> None
|
||||||
|
| None -> aux (n-1) (* retry *)
|
||||||
|
| Some _ as res -> res
|
||||||
|
in
|
||||||
|
aux max
|
||||||
|
|
||||||
|
let rec try_successively l fuel st = match l with
|
||||||
|
| [] -> None
|
||||||
|
| g :: l' ->
|
||||||
|
begin match g fuel st with
|
||||||
|
| None -> try_successively l' fuel st
|
||||||
|
| Some _ as res -> res
|
||||||
|
end
|
||||||
|
|
||||||
|
let (<?>) a b = try_successively [a;b]
|
||||||
|
|
||||||
|
let rec fix f fuel st = f (fix f) fuel st
|
||||||
|
|
||||||
|
let lift g fuel st = _consume 1 fuel (g st)
|
||||||
|
|
||||||
|
let lift' d g fuel st = _consume d fuel (g st)
|
||||||
|
|
||||||
|
let run ?(fuel=fun st -> Random.State.int st 40) f st =
|
||||||
|
match f (fuel st) st with
|
||||||
|
| None -> None
|
||||||
|
| Some (_fuel, x) -> Some x
|
||||||
|
|
||||||
|
exception GenFailure
|
||||||
|
|
||||||
|
let run_exn ?fuel f st =
|
||||||
|
match run ?fuel f st with
|
||||||
|
| None -> raise GenFailure
|
||||||
|
| Some x -> x
|
||||||
|
end
|
||||||
142
core/CCRandom.mli
Normal file
142
core/CCRandom.mli
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
|
||||||
|
(*
|
||||||
|
copyright (c) 2013-2014, simon cruanes
|
||||||
|
all rights reserved.
|
||||||
|
|
||||||
|
redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer. redistributions in binary
|
||||||
|
form must reproduce the above copyright notice, this list of conditions and the
|
||||||
|
following disclaimer in the documentation and/or other materials provided with
|
||||||
|
the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
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 Random Generators} *)
|
||||||
|
|
||||||
|
type state = Random.State.t
|
||||||
|
|
||||||
|
type 'a t = state -> 'a
|
||||||
|
(** Random generator for values of type ['a] *)
|
||||||
|
|
||||||
|
type 'a random_gen = 'a t
|
||||||
|
|
||||||
|
val return : 'a -> 'a t
|
||||||
|
(** [return x] is the generator that always returns [x].
|
||||||
|
Example: [let random_int = return 4 (* fair dice roll *)] *)
|
||||||
|
|
||||||
|
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
|
||||||
|
|
||||||
|
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||||||
|
|
||||||
|
val map : ('a -> 'b) -> 'a t -> 'b t
|
||||||
|
|
||||||
|
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
|
||||||
|
|
||||||
|
val choose : 'a t list -> 'a t
|
||||||
|
(** Choose a generator within the list.
|
||||||
|
@raise Invalid_argument if the list is empty *)
|
||||||
|
|
||||||
|
val choose_array : 'a t array -> 'a t
|
||||||
|
|
||||||
|
val choose_return : 'a list -> 'a t
|
||||||
|
(** Choose among the list
|
||||||
|
@raise Invalid_argument if the list is empty *)
|
||||||
|
|
||||||
|
(** {2 Fuel and Backtracking} *)
|
||||||
|
|
||||||
|
module Fuel : sig
|
||||||
|
type fuel = int
|
||||||
|
(** The fuel is a value that represents some "resource" used by the
|
||||||
|
random generator. *)
|
||||||
|
|
||||||
|
val split : fuel -> (fuel * fuel) option t
|
||||||
|
(** Split a (fuel) value [n] into [n1,n2] where [n = n1 + n2].
|
||||||
|
@return [None] if the value is too small *)
|
||||||
|
|
||||||
|
val split_list : fuel -> len:int -> fuel list option t
|
||||||
|
(** Split a (fuel) value [n] into a list of values whose sum is [n]
|
||||||
|
and whose length is [length].
|
||||||
|
@return [None] if the value is too small *)
|
||||||
|
|
||||||
|
(** {6 Fueled Generators} *)
|
||||||
|
|
||||||
|
type 'a t = fuel -> state -> (fuel * 'a) option
|
||||||
|
(** Fueled generators use some fuel to generate a value.
|
||||||
|
Can fail by lack of fuel. *)
|
||||||
|
|
||||||
|
val return : 'a -> 'a t
|
||||||
|
(** [return x] is the generator that always returns [x], and consumes one
|
||||||
|
fuel doing it. *)
|
||||||
|
|
||||||
|
val return' : fuel -> 'a -> 'a t
|
||||||
|
(** [return' f x] returns [x] but also consumes [fuel]. *)
|
||||||
|
|
||||||
|
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
|
||||||
|
|
||||||
|
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||||||
|
|
||||||
|
val map : ('a -> 'b) -> 'a t -> 'b t
|
||||||
|
|
||||||
|
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
|
||||||
|
|
||||||
|
val consume : unit t
|
||||||
|
(** Consume one fuel value *)
|
||||||
|
|
||||||
|
val consume' : fuel -> unit t
|
||||||
|
(** Consume the given amount of fuel *)
|
||||||
|
|
||||||
|
val fail : 'a t
|
||||||
|
(** Always fails. *)
|
||||||
|
|
||||||
|
val retry : ?max:int -> 'a t -> 'a t
|
||||||
|
(** [retry g] calls [g] until it returns some value, or until the maximum
|
||||||
|
number of retries was reached. If [g] fails,
|
||||||
|
then it counts for one iteration, and the generator retries.
|
||||||
|
@param max: maximum number of retries. Default [10] *)
|
||||||
|
|
||||||
|
val try_successively : 'a t list -> 'a t
|
||||||
|
(** [try_successively l] tries each generator of [l], one after the other.
|
||||||
|
If some generator succeeds its result is returned, else the
|
||||||
|
next generator is tried *)
|
||||||
|
|
||||||
|
val (<?>) : 'a t -> 'a t -> 'a t
|
||||||
|
(** [a <?> b] is a choice operator. It first tries [a], and returns its
|
||||||
|
result if successful. If [a] fails, then [b] is returned. *)
|
||||||
|
|
||||||
|
val fix : ('a t -> 'a t) -> 'a t
|
||||||
|
(** Recursion combinators, for building (fueled) recursive values *)
|
||||||
|
|
||||||
|
val lift : 'a random_gen -> 'a t
|
||||||
|
(** lifts a regular random generator into a fueled one, that consumes
|
||||||
|
one fuel unit *)
|
||||||
|
|
||||||
|
val lift' : fuel -> 'a random_gen -> 'a t
|
||||||
|
(** lifts a regular random generator into a fueled one, that consumes
|
||||||
|
one fuel unit *)
|
||||||
|
|
||||||
|
(** {6 Running} *)
|
||||||
|
|
||||||
|
val run : ?fuel:fuel random_gen -> 'a t -> 'a option random_gen
|
||||||
|
(** Run the given fueled generator with an amount of fuel
|
||||||
|
given by the [fuel] generator.
|
||||||
|
@return None if the *)
|
||||||
|
|
||||||
|
exception GenFailure
|
||||||
|
|
||||||
|
val run_exn : ?fuel:fuel random_gen -> 'a t -> 'a random_gen
|
||||||
|
(** Same as {!run}, but in case of failure it raises an exception.
|
||||||
|
@raise GenFailure in case the generator fails *)
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue