Make CCSeq.to_array behave better with stateful sequences (#390)

Make Seq.to_array only traverse seq once

This PR suggests a change to `Seq.to_array`, which uses construction of
an intermediate list to prevent traversing the Seq twice. This requires
the allocation of an intermediate list, but it eliminates the surprising
behavior that otherwise occurs with state-full sequences, due to the
extra traversal required to obtain the length of the sequence. E.g.,
with the previous implementation, the value of `to_array` for the
sequence constructed in the test added in this commit is `[|4;5;6|]`,
while `to_list` gives `[|1;2;3|]`.
This commit is contained in:
Shon Feder 2021-12-11 21:28:10 -05:00 committed by GitHub
parent 74954f53a0
commit 946ac4e05d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 16 additions and 9 deletions

View file

@ -395,14 +395,18 @@ let of_array a =
aux a 0
let to_array l =
match l() with
| Nil -> [| |]
| Cons (x, _) ->
let n = length l in
let a = Array.make n x in (* need first elem to create [a] *)
iteri
(fun i x -> a.(i) <- x)
l;
(* We contruct the length and list of seq elements (in reverse) in one pass *)
let len, ls = fold_left (fun (i, acc) x -> (i + 1, x :: acc)) (0, []) l in
(* The length is used to initialize the array, and then to derive the index for
each item, working back from the last. This lets us only traverse the list
twice, instead of having to reverse it. *)
match ls with
| [] -> [||]
| init::rest ->
let a = Array.make len init in
(* Subtract 1 for len->index conversion and 1 for the removed [init] *)
let idx = len - 2 in
ignore (List.fold_left (fun i x -> a.(i) <- x; i - 1) idx rest : int);
a
(*$Q
@ -412,6 +416,9 @@ let to_array l =
(*$T
of_array [| 1; 2; 3 |] |> to_list = [1;2;3]
of_list [1;2;3] |> to_array = [| 1; 2; 3; |]
let r = ref 1 in \
let s = unfold (fun i -> if i < 3 then let x = !r in incr r; Some (x, succ i) else None) 0 in \
to_array s = [| 1; 2; 3; |]
*)
let rec to_iter res k = match res () with

View file

@ -270,7 +270,7 @@ val of_array : 'a array -> 'a t
@since 0.13 *)
val to_array : 'a t -> 'a array
(** Convert into array. Iterate twice.
(** Convert into array.
@since 0.13 *)
val to_rev_list : 'a t -> 'a list