Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/FSharpx.Collections/LazyList.fs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ module LazyList =
let consDelayed x l =
lzy(fun () -> (consc x (lzy(fun () -> (force(l()))))))

let consLazy x (l: Lazy<LazyList<'T>>) =
lzy(fun () -> consc x l.Value)

let uncons(s: LazyList<'T>) = s.Uncons

let tryUncons(s: LazyList<'T>) =
Expand Down
98 changes: 51 additions & 47 deletions src/FSharpx.Collections/LazyList.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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

[<RequireQualifiedAccess>]
module LazyList =
Expand All @@ -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 <c>Some(x)</c> 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 <c>true</c>.
/// Raise <c>KeyNotFoundException</c> 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
[<GeneralizableValue>]
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
/// <c>consDelayed</c> when a <c>Lazy</c> value is already available.
val consLazy: 'T -> Lazy<LazyList<'T>> -> 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>> -> 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>
Expand All @@ -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>
21 changes: 21 additions & 0 deletions tests/FSharpx.Collections.Tests/LazyListTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> = 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))
Expand Down
Loading