Documentation for gololang.LazyLists

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.

Augmentations

gololang.LazyList

drop(this, nb)

Remove nb elements from the list and return the rest as a lazy list.

dropWhile(this, pred)

Remove elements from the list as long as the given predicate is true.

filter(this, pred)

Filters elements based on a predicate.

Returns a new lazy list.

find(this, pred)

Finds the first element of a list matching a predicate:

println(lazyList(1, 2, 3, 4): find(|n| -> n > 3))

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.

foldl(this, func, zero)

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.

foldr(this, func, zero)

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.

join(this, separator)

Joins the elements into a string:

println(list[1, 2, 3]: join(", "))

The returned string is "" when the list is empty.

map(this, func)

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.

subList(this, from, to)

Extract a lazy sublist.

This is just a convenient method for list: drop(from):take(to - from), so the list remains lazy.

take(this, nb)

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.

takeWhile(this, pred)

Takes elements from the list as long as the given predicate is true.

java.lang.Iterable

asLazyList(this)

Returns a lazy list from this Iterable. Can be used for instance to lazily map a list.

java.util.Iterator

asLazyList(this)

Returns a lazy list view of this Iterator.

Functions

cons(ht)

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].

cons(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]

emptyList()

Returns the empty list.

fromIter(it)

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(unspool, finished, seed)

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
)

iterate(zero, func)

Returns 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)

lazyList(values...)

Variadic 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()))))

repeat(value)

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 5s, and repeat(^f) will return a infinite lazy list of calls to f ([f(), f(), f(), …])