diff --git a/src/FSharpx.Collections/LazyList.fs b/src/FSharpx.Collections/LazyList.fs index ab0c935f..b4d4500f 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>) = + notlazy(CellCons(x, lzy(fun () -> force 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 8c99f156..171e9cd7 100644 --- a/src/FSharpx.Collections/LazyList.fsi +++ b/src/FSharpx.Collections/LazyList.fsi @@ -138,6 +138,11 @@ module LazyList = /// followed by the list returned by the given computation. The 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> diff --git a/tests/FSharpx.Collections.Tests/LazyListTests.fs b/tests/FSharpx.Collections.Tests/LazyListTests.fs index d4df9fdc..e197ba59 100644 --- a/tests/FSharpx.Collections.Tests/LazyListTests.fs +++ b/tests/FSharpx.Collections.Tests/LazyListTests.fs @@ -317,6 +317,37 @@ 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 + let mutable tailForced = false + + let tail = + lazy + (tailForced <- true + LazyList.ofList [ 2; 3 ]) + + let ll = LazyList.consLazy 1 tail + Expect.isFalse "consLazy divergence: construction should not force the tail" tailForced + let _ = LazyList.head ll + Expect.isFalse "consLazy divergence: head should not force the tail" tailForced + } + + 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))