From 946ac4e05d531afcbbdf957900ba519091d67e0d Mon Sep 17 00:00:00 2001 From: Shon Feder Date: Sat, 11 Dec 2021 21:28:10 -0500 Subject: [PATCH] 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|]`. --- src/core/CCSeq.ml | 23 +++++++++++++++-------- src/core/CCSeq.mli | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/core/CCSeq.ml b/src/core/CCSeq.ml index 7f910857..55a32593 100644 --- a/src/core/CCSeq.ml +++ b/src/core/CCSeq.ml @@ -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 diff --git a/src/core/CCSeq.mli b/src/core/CCSeq.mli index 6d93b147..1719ba92 100644 --- a/src/core/CCSeq.mli +++ b/src/core/CCSeq.mli @@ -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