diff --git a/src/FSharpx.Collections/LazyList.fs b/src/FSharpx.Collections/LazyList.fs index ab0c935f..fbf4511f 100644 --- a/src/FSharpx.Collections/LazyList.fs +++ b/src/FSharpx.Collections/LazyList.fs @@ -114,6 +114,9 @@ module LazyList = let consDelayed x l = lzy(fun () -> (consc x (lzy(fun () -> (force(l())))))) + let consLazy x (l: Lazy>) = + lzy(fun () -> consc x l.Value) + let uncons(s: LazyList<'T>) = s.Uncons let tryUncons(s: LazyList<'T>) = diff --git a/src/FSharpx.Collections/LazyList.fsi b/src/FSharpx.Collections/LazyList.fsi index d039e8d7..171e9cd7 100644 --- a/src/FSharpx.Collections/LazyList.fsi +++ b/src/FSharpx.Collections/LazyList.fsi @@ -28,32 +28,32 @@ type LazyList<'T> = ///O(1). Test if a list is empty. Forces the evaluation of /// the first element of the stream if it is not already evaluated. - member IsEmpty : bool + member IsEmpty: bool ///O(1). Return the first element of the list. Forces the evaluation of /// the first cell of the list if it is not already evaluated. - member Head : 'T + member Head: 'T ///O(n). Return the length of the list - member Length : unit -> int + member Length: unit -> int ///O(1). Return option the first element of the list. Forces the evaluation of /// the first cell of the list if it is not already evaluated. - member TryHead : 'T option + member TryHead: 'T option ///O(1). Return the list corresponding to the remaining items in the sequence. /// Forces the evaluation of the first cell of the list if it is not already evaluated. - member Tail : LazyList<'T> + member Tail: LazyList<'T> ///O(1). Return option the list corresponding to the remaining items in the sequence. /// Forces the evaluation of the first cell of the list if it is not already evaluated. - member TryTail : LazyList<'T> option + member TryTail: LazyList<'T> option ///O(1). Returns tuple of head element and tail of the list. - member Uncons : 'T * LazyList<'T> + member Uncons: 'T * LazyList<'T> ///O(1). Returns option tuple of head element and tail of the list. - member TryUncons : ('T * LazyList<'T>) option + member TryUncons: ('T * LazyList<'T>) option [] module LazyList = @@ -64,141 +64,146 @@ module LazyList = ///O(1). Return the first element of the list. Forces the evaluation of /// the first cell of the list if it is not already evaluated. - val head : LazyList<'T> -> 'T + val head: LazyList<'T> -> 'T ///O(1). Return option the first element of the list. Forces the evaluation of /// the first cell of the list if it is not already evaluated. - val tryHead : LazyList<'T> -> 'T option + val tryHead: LazyList<'T> -> 'T option ///O(1). Return the list corresponding to the remaining items in the sequence. /// Forces the evaluation of the first cell of the list if it is not already evaluated. - val tail : LazyList<'T> -> LazyList<'T> + val tail: LazyList<'T> -> LazyList<'T> ///O(1). Return option the list corresponding to the remaining items in the sequence. /// Forces the evaluation of the first cell of the list if it is not already evaluated. - val tryTail : LazyList<'T> -> LazyList<'T> option + val tryTail: LazyList<'T> -> LazyList<'T> option ///O(1). Returns tuple of head element and tail of the list. - val uncons : LazyList<'T> -> 'T * LazyList<'T> + val uncons: LazyList<'T> -> 'T * LazyList<'T> ///O(1). Returns option tuple of head element and tail of the list. - val tryUncons : LazyList<'T> -> ('T * LazyList<'T>) option + val tryUncons: LazyList<'T> -> ('T * LazyList<'T>) option ///O(n), where n is count. Return the list which on consumption will consist of at most 'n' elements of /// the input list. - val take : count:int -> source:LazyList<'T> -> LazyList<'T> + val take: count: int -> source: LazyList<'T> -> LazyList<'T> ///O(n), where n is count. Return the list which on consumption will remove of at most 'n' elements of /// the input list. - val drop : count:int -> source:LazyList<'T> -> LazyList<'T> + val drop: count: int -> source: LazyList<'T> -> LazyList<'T> ///O(n), where n is count. Return the list which on consumption will consist of at most 'n' elements of /// the input list. - val tryTake : count:int -> source:LazyList<'T> -> LazyList<'T> option + val tryTake: count: int -> source: LazyList<'T> -> LazyList<'T> option ///O(n), where n is count. Return the list which on consumption will skip the first 'n' elements of /// the input list. - val skip : count:int -> source:LazyList<'T> -> LazyList<'T> + val skip: count: int -> source: LazyList<'T> -> LazyList<'T> ///O(n), where n is count. Return option the list which skips the first 'n' elements of /// the input list. - val trySkip : count:int -> source:LazyList<'T> -> LazyList<'T> option + val trySkip: count: int -> source: LazyList<'T> -> LazyList<'T> option ///O(n). /// it applies a function to each element of a list, /// passing an accumulating parameter from left to right, - val fold : f:('T1 -> 'T2 -> 'T1) -> s:'T1 -> l:LazyList<'T2> -> 'T1 + val fold: f: ('T1 -> 'T2 -> 'T1) -> s: 'T1 -> l: LazyList<'T2> -> 'T1 ///O(n). Behaves like a combination of map and fold; /// it applies a function to each element of a list, /// passing an accumulating parameter from left to right, /// and returning a final value of this accumulator together with the new list. - val mapAccum : f:('T1 -> 'T2 -> 'T1 * 'T3) -> s:'T1 -> l:LazyList<'T2> -> 'T1 * LazyList<'T3> + val mapAccum: f: ('T1 -> 'T2 -> 'T1 * 'T3) -> s: 'T1 -> l: LazyList<'T2> -> 'T1 * LazyList<'T3> ///O(n), worst case. Apply the given function to successive elements of the list, returning the first /// result where function returns Some(x) for some x. If the function never returns /// true, 'None' is returned. - val tryFind : predicate:('T -> bool) -> source:LazyList<'T> -> 'T option + val tryFind: predicate: ('T -> bool) -> source: LazyList<'T> -> 'T option ///O(n), worst case. Return the first element for which the given function returns true. /// Raise KeyNotFoundException if no such element exists. - val find : predicate:('T -> bool) -> source:LazyList<'T> -> 'T + val find: predicate: ('T -> bool) -> source: LazyList<'T> -> 'T ///O(1). Evaluates to the list that contains no items [] - val empty<'T> : LazyList<'T> + val empty<'T> : LazyList<'T> ///O(n). Return the length of the list - val length: list:LazyList<'T> -> int + val length: list: LazyList<'T> -> int ///O(1). Return a new list which contains the given item followed by the /// given list. - val cons : 'T -> LazyList<'T> -> LazyList<'T> + val cons: 'T -> LazyList<'T> -> LazyList<'T> ///O(1). Return a new list which on consumption contains the given item /// followed by the list returned by the given computation. The - val consDelayed : 'T -> (unit -> LazyList<'T>) -> LazyList<'T> + val consDelayed: 'T -> (unit -> LazyList<'T>) -> LazyList<'T> + + ///O(1). Return a new list which on consumption contains the given item + /// followed by the list wrapped in the given lazy value. More efficient than + /// consDelayed when a Lazy value is already available. + val consLazy: 'T -> Lazy> -> LazyList<'T> ///O(1). Return the list which on consumption will consist of an infinite sequence of /// the given item - val repeat : 'T -> LazyList<'T> + val repeat: 'T -> LazyList<'T> ///O(1). Return a list that is in effect the list returned by the given computation. /// The given computation is not executed until the first element on the list is /// consumed. - val delayed : (unit -> LazyList<'T>) -> LazyList<'T> + val delayed: (unit -> LazyList<'T>) -> LazyList<'T> ///O(1). Return a list that contains the elements returned by the given computation. /// The given computation is not executed until the first element on the list is /// consumed. The given argument is passed to the computation. Subsequent elements /// in the list are generated by again applying the residual 'b to the computation. - val unfold : ('State -> ('T * 'State) option) -> 'State -> LazyList<'T> + val unfold: ('State -> ('T * 'State) option) -> 'State -> LazyList<'T> ///O(1). Return the list which contains on demand the elements of the first list followed /// by the elements of the second list - val append : LazyList<'T> -> source:LazyList<'T> -> LazyList<'T> + val append: LazyList<'T> -> source: LazyList<'T> -> LazyList<'T> ///O(1). Return the list which contains on demand the pair of elements of the first and second list - val zip : LazyList<'T1> -> LazyList<'T2> -> LazyList<'T1 * 'T2> + val zip: LazyList<'T1> -> LazyList<'T2> -> LazyList<'T1 * 'T2> ///O(1). Return the list which contains on demand the list of elements of the list of lazy lists. - val concat : LazyList< LazyList<'T>> -> LazyList<'T> + val concat: LazyList> -> LazyList<'T> /// Splits the list at the gicen index. - val split : LazyList<'T> -> int -> ('T list * LazyList<'T>) + val split: LazyList<'T> -> int -> ('T list * LazyList<'T>) ///O(1). Return a new collection which on consumption will consist of only the elements of the collection /// for which the given predicate returns "true" - val filter : predicate:('T -> bool) -> source:LazyList<'T> -> LazyList<'T> + val filter: predicate: ('T -> bool) -> source: LazyList<'T> -> LazyList<'T> ///O(n). Apply the given function to each element of the collection. - val iter: action:('T -> unit) -> list:LazyList<'T>-> unit + val iter: action: ('T -> unit) -> list: LazyList<'T> -> unit ///O(1). Return a new list consisting of the results of applying the given accumulating function /// to successive elements of the list - val scan : folder:('State -> 'T -> 'State) -> 'State -> source:LazyList<'T> -> LazyList<'State> + val scan: folder: ('State -> 'T -> 'State) -> 'State -> source: LazyList<'T> -> LazyList<'State> ///O(1). Build a new collection whose elements are the results of applying the given function /// to each of the elements of the collection. - val map : mapping:('T -> 'U) -> source:LazyList<'T> -> LazyList<'U> + val map: mapping: ('T -> 'U) -> source: LazyList<'T> -> LazyList<'U> ///O(1). Build a new collection whose elements are the results of applying the given function /// to the corresponding elements of the two collections pairwise. - val map2 : mapping:('T1 -> 'T2 -> 'U) -> LazyList<'T1> -> LazyList<'T2> -> LazyList<'U> + val map2: mapping: ('T1 -> 'T2 -> 'U) -> LazyList<'T1> -> LazyList<'T2> -> LazyList<'U> ///O(1). Build a collection from the given array. This function will eagerly evaluate all of the /// list (and thus may not terminate). - val ofArray : 'T array -> LazyList<'T> + val ofArray: 'T array -> LazyList<'T> ///O(n). Build an array from the given collection - val toArray : LazyList<'T> -> 'T array + val toArray: LazyList<'T> -> 'T array ///O(1). Build a collection from the given list. This function will eagerly evaluate all of the /// list (and thus may not terminate). - val ofList : list<'T> -> LazyList<'T> + val ofList: list<'T> -> LazyList<'T> ///O(n). Build a non-lazy list from the given collection. This function will eagerly evaluate all of the /// list (and thus may not terminate). - val toList : LazyList<'T> -> list<'T> + val toList: LazyList<'T> -> list<'T> ///O(n). Return a view of the collection as an enumerable object val toSeq: LazyList<'T> -> seq<'T> @@ -211,14 +216,13 @@ module LazyList = ///O(n). Compares two lazy lists using the given comparison function, element by element. /// Both lists are evaluated until one of them is empty. - val compareWith : ('T -> 'T -> int) -> LazyList<'T> -> LazyList<'T> -> int + val compareWith: ('T -> 'T -> int) -> LazyList<'T> -> LazyList<'T> -> int ///O(n). Checks if two lazy lists are equal using the given equality function, element by element. /// Both lists are evaluated until one of them is empty. - val equalsWith : ('T -> 'T -> bool) -> LazyList<'T> -> LazyList<'T> -> bool + val equalsWith: ('T -> 'T -> bool) -> LazyList<'T> -> LazyList<'T> -> bool //-------------------------------------------------------------------------- // Lazy list active patterns - val (|Cons|Nil|) : LazyList<'T> -> Choice<('T * LazyList<'T>),unit> - + val (|Cons|Nil|): LazyList<'T> -> Choice<('T * LazyList<'T>), unit> diff --git a/tests/FSharpx.Collections.Tests/LazyListTests.fs b/tests/FSharpx.Collections.Tests/LazyListTests.fs index abdd1bdb..39d8316b 100644 --- a/tests/FSharpx.Collections.Tests/LazyListTests.fs +++ b/tests/FSharpx.Collections.Tests/LazyListTests.fs @@ -317,6 +317,27 @@ module LazyList = test "dropDiverge1" { Expect.isTrue "divergence" (let ss = LazyList.skip 1 (LazyList.consDelayed 1 diverge) in true) } (* testing for lazy divergence *) test "dropDiverge0" { Expect.isTrue "divergence" (let ss = LazyList.skip 0 (LazyList.delayed(fun () -> failwith "errors")) in true) } (* testing for lazy divergence *) + test "consLazy head" { + let tail = lazy (LazyList.ofList [ 2; 3 ]) + Expect.equal "consLazy head" 1 (LazyList.head(LazyList.consLazy 1 tail)) + } + + test "consLazy toList" { + let tail = lazy (LazyList.ofList [ 2; 3 ]) + Expect.equal "consLazy toList" [ 1; 2; 3 ] (LazyList.toList(LazyList.consLazy 1 tail)) + } + + test "consLazy lazy divergence" { + // tail is not evaluated unless the tail is consumed + Expect.isTrue "consLazy divergence" (let _ = LazyList.consLazy 1 (lazy (failwith "diverge")) in true) + } + + test "consLazy infinite" { + // build ones = 1 :: 1 :: ... using consLazy + let rec ones: LazyList = LazyList.consLazy 1 (lazy ones) + Expect.equal "consLazy infinite" [ 1; 1; 1; 1; 1 ] (LazyList.take 5 ones |> LazyList.toList) + } + test "takedrop" { Expect.equal "takedrop" [ 4; 5; 6 ] <| LazyList.toList(LazyList.take 3 (LazyList.skip 4 nats))