Skip to content

Commit

Permalink
Added fold_{left, right}, exists and for_all to String/Bytes (#882)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluddy committed Apr 29, 2021
1 parent b6ef1ef commit 9818f7a
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ Working version
(Gabriel Scherer, review by Ulugbek Abdullaev and Daniel Bünzli
and Nicolás Ojeda Bär and Florian Angeletti)

- #882: Add fold_left, fold_right, exists and for_all to String/Bytes
(Yotam Barnoy, review by Alain Frisch and Jeremy Yallop)

### Other libraries:

- #10047: Add `Unix.realpath`
Expand Down
30 changes: 30 additions & 0 deletions stdlib/bytes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,36 @@ let mapi f s =
r
end

let fold_left f x a =
let r = ref x in
for i = 0 to length a - 1 do
r := f !r (unsafe_get a i)
done;
!r

let fold_right f a x =
let r = ref x in
for i = length a - 1 downto 0 do
r := f (unsafe_get a i) !r
done;
!r

let exists p s =
let n = length s in
let rec loop i =
if i = n then false
else if p (unsafe_get s i) then true
else loop (succ i) in
loop 0

let for_all p s =
let n = length s in
let rec loop i =
if i = n then true
else if p (unsafe_get s i) then loop (succ i)
else false in
loop 0

let uppercase_ascii s = map Char.uppercase_ascii s
let lowercase_ascii s = map Char.lowercase_ascii s

Expand Down
21 changes: 21 additions & 0 deletions stdlib/bytes.mli
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,27 @@ val mapi : (int -> char -> char) -> bytes -> bytes
index (in increasing index order) and stores the resulting bytes
in a new sequence that is returned as the result. *)

val fold_left : ('a -> char -> 'a) -> 'a -> bytes -> 'a
(** [fold_left f x s] computes
[f (... (f (f x (get s 0)) (get s 1)) ...) (get s (n-1))],
where [n] is the length of [s].
@since 4.13.0 *)

val fold_right : (char -> 'a -> 'a) -> bytes -> 'a -> 'a
(** [fold_right f s x] computes
[f (get s 0) (f (get s 1) ( ... (f (get s (n-1)) x) ...))],
where [n] is the length of [s].
@since 4.13.0 *)

val for_all : (char -> bool) -> bytes -> bool
(** [for_all p s] checks if all characters in [s] satisfy the predicate [p].
@since 4.13.0 *)

val exists : (char -> bool) -> bytes -> bool
(** [exists p s] checks if at least one character of [s] satisfies the predicate
[p].
@since 4.13.0 *)

val trim : bytes -> bytes
(** Return a copy of the argument, without leading and trailing
whitespace. The bytes regarded as whitespace are the ASCII
Expand Down
21 changes: 21 additions & 0 deletions stdlib/bytesLabels.mli
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,27 @@ val mapi : f:(int -> char -> char) -> bytes -> bytes
index (in increasing index order) and stores the resulting bytes
in a new sequence that is returned as the result. *)

val fold_left : f:('a -> char -> 'a) -> init:'a -> bytes -> 'a
(** [fold_left f x s] computes
[f (... (f (f x (get s 0)) (get s 1)) ...) (get s (n-1))],
where [n] is the length of [s].
@since 4.13.0 *)

val fold_right : f:(char -> 'a -> 'a) -> bytes -> init:'a -> 'a
(** [fold_right f s x] computes
[f (get s 0) (f (get s 1) ( ... (f (get s (n-1)) x) ...))],
where [n] is the length of [s].
@since 4.13.0 *)

val for_all : f:(char -> bool) -> bytes -> bool
(** [for_all p s] checks if all characters in [s] satisfy the predicate [p].
@since 4.13.0 *)

val exists : f:(char -> bool) -> bytes -> bool
(** [exists p s] checks if at least one character of [s] satisfies the predicate
[p].
@since 4.13.0 *)

val trim : bytes -> bytes
(** Return a copy of the argument, without leading and trailing
whitespace. The bytes regarded as whitespace are the ASCII
Expand Down
8 changes: 8 additions & 0 deletions stdlib/string.ml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ let map f s =
B.map f (bos s) |> bts
let mapi f s =
B.mapi f (bos s) |> bts
let fold_right f x a =
B.fold_right f (bos x) a
let fold_left f a x =
B.fold_left f a (bos x)
let exists f s =
B.exists f (bos s)
let for_all f s =
B.for_all f (bos s)

(* Beware: we cannot use B.trim or B.escape because they always make a
copy, but String.mli spells out some cases where we are not allowed
Expand Down
19 changes: 19 additions & 0 deletions stdlib/string.mli
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,25 @@ val mapi : (int -> char -> char) -> string -> string
@since 4.02.0 *)

val fold_left : ('a -> char -> 'a) -> 'a -> string -> 'a
(** [fold_left f x s] computes [f (... (f (f x s.[0]) s.[1]) ...) s.[n-1]],
where [n] is the length of the string [s].
@since 4.13.0 *)

val fold_right : (char -> 'a -> 'a) -> string -> 'a -> 'a
(** [fold_right f s x] computes [f s.[0] (f s.[1] ( ... (f s.[n-1] x) ...))],
where [n] is the length of the string [s].
@since 4.13.0 *)

val for_all : (char -> bool) -> string -> bool
(** [for_all p s] checks if all characters in [s] satisfy the predicate [p].
@since 4.13.0 *)

val exists : (char -> bool) -> string -> bool
(** [exists p s] checks if at least one character of [s] satisfies the predicate
[p].
@since 4.13.0 *)

val trim : string -> string
(** [trim s] is [s] without leading and trailing whitespace. Whitespace
characters are: [' '], ['\x0C'] (form feed), ['\n'], ['\r'], and ['\t'].
Expand Down
19 changes: 19 additions & 0 deletions stdlib/stringLabels.mli
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,25 @@ val mapi : f:(int -> char -> char) -> string -> string
@since 4.02.0 *)

val fold_left : f:('a -> char -> 'a) -> init:'a -> string -> 'a
(** [fold_left f x s] computes [f (... (f (f x s.[0]) s.[1]) ...) s.[n-1]],
where [n] is the length of the string [s].
@since 4.13.0 *)

val fold_right : f:(char -> 'a -> 'a) -> string -> init:'a -> 'a
(** [fold_right f s x] computes [f s.[0] (f s.[1] ( ... (f s.[n-1] x) ...))],
where [n] is the length of the string [s].
@since 4.13.0 *)

val for_all : f:(char -> bool) -> string -> bool
(** [for_all p s] checks if all characters in [s] satisfy the predicate [p].
@since 4.13.0 *)

val exists : f:(char -> bool) -> string -> bool
(** [exists p s] checks if at least one character of [s] satisfies the predicate
[p].
@since 4.13.0 *)

val trim : string -> string
(** [trim s] is [s] without leading and trailing whitespace. Whitespace
characters are: [' '], ['\x0C'] (form feed), ['\n'], ['\r'], and ['\t'].
Expand Down
31 changes: 31 additions & 0 deletions testsuite/tests/lib-bytes/test_bytes.ml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,37 @@ let () =
length r = 7
&& check r 1 "abcde");

(*
abcde
edcba
*)
Testing.test
(let r = copy abcde in
let l = fold_left (fun acc x -> (make 1 x)::acc) [] r in
let result = concat (Bytes.of_string "") l in
length result = 5
&& check result 0 "edcba");

(*
abcde
abcde
*)
Testing.test
(let r = copy abcde in
let l = fold_right (fun x acc -> (make 1 x)::acc) r [] in
let result = concat (Bytes.of_string "") l in
length result = 5
&& check result 0 "abcde");

(*
test exists and for_all
*)
Testing.test
(exists (fun c -> c = 'b') abcde
&& not (exists (fun c -> c = 'f') abcde)
&& for_all (fun c -> c <> 'f') abcde
&& not (for_all (fun c -> c = 'b') abcde));

(* length + left + right < 0 *)
test_raises_invalid_argument
(fun () -> extend abcde (-3) (-3)) ();
Expand Down
2 changes: 1 addition & 1 deletion testsuite/tests/lib-bytes/test_bytes.reference
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
All tests succeeded.

0 comments on commit 9818f7a

Please sign in to comment.