add CCInt.range{,',by} for iterating on integer ranges

This commit is contained in:
Simon Cruanes 2017-03-29 17:40:19 +02:00
parent 47abc78a51
commit e0287b9efe
2 changed files with 99 additions and 0 deletions

View file

@ -39,6 +39,7 @@ let pow a b =
type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a
type 'a sequence = ('a -> unit) -> unit
let random n st = Random.State.int st n
let random_small = random 100
@ -96,6 +97,76 @@ let to_string_binary n =
Q.int (fun n -> n = int_of_string (to_string_binary n))
*)
let range_by ~step i j yield =
let rec range i j yield =
if i=j then yield i
else (
yield i;
range (i+step) j yield
)
in
if step = 0 then
raise (Invalid_argument "CCList.range_by")
else if (if step > 0 then i>j else i<j) then ()
else range i ((j-i)/step*step + i) yield
(* note: the last test checks that no error occurs due to overflows. *)
(*$= & ~printer:Q.Print.(list int)
[0] (range_by ~step:1 0 0 |> Sequence.to_list)
[] (range_by ~step:1 5 0 |> Sequence.to_list)
[] (range_by ~step:2 1 0 |> Sequence.to_list)
[0;2;4] (range_by ~step:2 0 4 |> Sequence.to_list)
[0;2;4] (range_by ~step:2 0 5 |> Sequence.to_list)
[0] (range_by ~step:~-1 0 0 |> Sequence.to_list)
[] (range_by ~step:~-1 0 5 |> Sequence.to_list)
[] (range_by ~step:~-2 0 1 |> Sequence.to_list)
[5;3;1] (range_by ~step:~-2 5 1 |> Sequence.to_list)
[5;3;1] (range_by ~step:~-2 5 0 |> Sequence.to_list)
[0] (range_by ~step:max_int 0 2 |> Sequence.to_list)
*)
(*$Q
Q.(pair small_int small_int) (fun (i,j) -> \
let i = min i j and j = max i j in \
CCList.equal CCInt.equal \
(CCInt.range_by ~step:1 i j |> Sequence.to_list) \
(CCInt.range i j |> Sequence.to_list) )
*)
let range i j yield =
let rec up i j yield =
if i=j then yield i
else (
yield i;
up (i+1) j yield
)
and down i j yield =
if i=j then yield i
else (
yield i;
down (i-1) j yield
)
in
if i<=j then up i j yield else down i j yield
(*$= & ~printer:Q.Print.(list int)
[0;1;2;3;4;5] (range 0 5 |> Sequence.to_list)
[0] (range 0 0 |> Sequence.to_list)
[5;4;3;2] (range 5 2 |> Sequence.to_list)
*)
let range' i j yield =
if i<j then range i (j-1) yield
else if i=j then ()
else range i (j+1) yield
(*$= & ~printer:Q.Print.(list int)
[] (range' 0 0 |> Sequence.to_list)
[0;1;2;3;4] (range' 0 5 |> Sequence.to_list)
[5;4;3] (range' 5 2 |> Sequence.to_list)
*)
module Infix = struct
let (=) = Pervasives.(=)
let (<>) = Pervasives.(<>)
@ -103,6 +174,8 @@ module Infix = struct
let (>) = Pervasives.(>)
let (<=) = Pervasives.(<=)
let (>=) = Pervasives.(>=)
let (--) = range
let (--^) = range'
end
include Infix
let min = min

View file

@ -25,6 +25,7 @@ val pow : t -> t -> t
type 'a printer = Format.formatter -> 'a -> unit
type 'a random_gen = Random.State.t -> 'a
type 'a sequence = ('a -> unit) -> unit
val random : int -> t random_gen
val random_small : t random_gen
@ -51,6 +52,23 @@ val min : t -> t -> t
val max : t -> t -> t
(** @since 0.17 *)
val range_by : step:t -> t -> t -> t sequence
(** [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 NEXT_RELEASE *)
val range : t -> t -> t sequence
(** [range i j] iterates on integers from [i] to [j] included . It works
both for decreasing and increasing ranges
@since NEXT_RELEASE *)
val range' : t -> t -> t sequence
(** Same as {!range} but the second bound is excluded.
For instance [range' 0 5 = Sequence.of_list [0;1;2;3;4]]
@since NEXT_RELEASE *)
(** {2 Infix Operators}
@since 0.17 *)
@ -72,6 +90,14 @@ module Infix : sig
val (>=) : t -> t -> bool
(** @since 0.17 *)
val (--) : t -> t -> t sequence
(** Alias to {!range}
@since NEXT_RELEASE *)
val (--^) : t -> t -> t sequence
(** Alias to {!range'}
@since NEXT_RELEASE *)
end
include module type of Infix