This module defines utility functions and augmentations to ease the use of
the gololang.LazyList
object.
A lazy list is an immutable list whose elements are evaluated only when needed, as can be found in Haskell for example.
This is very useful when using higher order function such as map
. Mapping
a long lazy list with a function and using only the 3 first elements will only
apply the function to these elements, as opposed to regular lists.
Lazy lists can also be used to create infinite lists, also known as generators (or anamorphisms).
Lastly, they allow for elegant recursive implementations of several classical algorithms.
For instance, one can create a infinite lazy list containing integers as:
function count = |start| -> cons(start, -> count(start + 1_L))
function count = -> count(0_L)
On the other hand, functions or methods like equals
, size
or contains
are
not very efficients, since they must evaluate the whole list, and thus negate
the laziness. They are here for completeness and compatibility with the regular
lists interface, but you should avoid such methods.
Some functions in this module are recursive (re)implementation of standard list
HOF, such as map
or filter
. The recursive aspect should not be limiting since
the resulting list is lazy.
Remove nb
elements from the list and return the rest as a lazy list.
Remove elements from the list as long as the given predicate is true.
pred
: the predicate function used to end the list.Filters elements based on a predicate.
Returns a new lazy list.
Finds the first element of a list matching a predicate:
println(lazyList(1, 2, 3, 4): find(|n| -> n > 3))
this
: a lazy list.pred
: a predicate function taking an element and returning a boolean.find
returns null
when no element satisfies pred
.
Note that in the worst case, all the list is search. Take care to not use this method on infinite list, since no check is made.
Folds left this
using func
with zero
as initial value.
This is a recursive implementation.
lazyList(a, b, c): foldl(f, z) == f(f(f(z, a), b), c)
Equivalent to foldr
if func
is commutative.
Folds right this
using func
with zero
as initial value.
This is a recursive implementation.
lazyList(a, b, c): foldr(f, z) == f(a, f(b, f(c, z)))
Equivalent to foldl
if func
is commutative.
Joins the elements into a string:
println(list[1, 2, 3]: join(", "))
this
: a list.separator
: the element separator string.The returned string is ""
when the list is empty.
Maps elements of a list using a function:
lazyList(1, 2, 3):map(|x| -> 2 * x)
map
returns a new lazy list, i.e. func
is applied only
when necessary.
This is a recursive implementation.
Extract a lazy sublist.
This is just a convenient method for list: drop(from):take(to - from)
, so
the list remains lazy.
from
: low endpoint (inclusive) of the subList
to
: high endpoint (exclusive) of the subList
Takes the nb
first elements of the lazy list, as a lazy list.
This is a wrapper, the underlying list is resolved on demand, such that
everything remains lazy. take
can thus be used on infinite lists.
Takes elements from the list as long as the given predicate is true.
pred
: the predicate function used to end the list.Returns a lazy list from this Iterable
. Can be used for instance to lazily
map a list.
Returns a lazy list view of this Iterator
.
Unary version of cons(head, tail)
.
Its parameter is assumed to be a tuple (or any object having a get(idx)
method)
of the form [head, tail]
.
Create a new lazy list from a head and tail values. Automatically wraps the tail in a closure if its not already one.
For example:
let myList = cons(1, cons(2, cons(3, emptyList())))
gives a lazy list equivalent to list[1, 2, 3]
Returns the empty list.
Wraps any object implementing Iterable
or Iterator
in a lazy list.
The next()
method of the underlying iterator is only called when the tail is
used.
NOTE:
If called with an Iterator
instance, the iterator is shared, so navigating
through the list can have side effects if another object is using the same
iterator.
Generator function on lazy lists (anamorphism).
This function generates a (possibly infinite) lazy list. Starting with the
seed
value, if finished(seed)
is true
, the generation stops and an empty
list is returned. Otherwise, unspool
is called on seed
, and must generate
two values: the head of the list (current value) and the next seed that will be
used to generate the tail.
As an example, one can write a simple range
function as:
let range = |start, end| -> generator(
|seed| -> [seed, seed + 1],
|seed| -> seed >= end,
start
)
unspool
: the generative functionfinished
: the condition functionseed
: the initial valueReturns an infinite lazy list produced by iterative application of a function
to an initial element.
iterate(z, f)
thus yields z, f(z), f(f(z)), ...
For instance, one can create a infinite list of integers using:
iterate(0, |x| -> x + 1)
zero
: the initial element of the listfunc
: the function to applyVariadic function to create lazy lists from values.
let myList = lazyList(1, 2, 3, 4)
is the equivalent to
let myList = cons(1, cons(2, cons(3, cons(4, emptyList()))))
Produces a infinite list of values. If the argument is a closure, it must have
no parameters, and it's used to produce the values (called for each tail
access).
For instance, repeat(5)
will return an infinite lazy list of 5
s, and
repeat(^f)
will return a infinite lazy list of calls to f
([f(), f(), f(), ...])
value
: a value or a closure