Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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

Comment on lines 29 to 39
///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)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot can you use mutable for that?

}

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