par::list<T> is a persistent (immutable) singly-linked list.
Operations such as push and pop do not modify the original list.
Instead, they return a new list that may share structure with the old one. Unlike most C++ data structures
(such as std::vector), copying a list is not expensive. Copying a list only copies a pointer to
the head node, so it takes constant time.
This data structure is similar to lists in SML. It is particularly useful when writing functional-style algorithms where previous versions of a list must remain valid.
For example, starting with a list L containing the elements [3,1,2],
writing L2 = L.push(5) produces a new list containig [5,3,1,2]
without changing the value of L. Under the hood, persistent lists achieve this
efficiently by sharing nodes that they have in common rather than creating deep copies.
lists| Default Constructor | Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
Creates an empty list.
par::list<int> L; // empty list
empty |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
par::list<int> L;
assert(L.empty());
L = L.push(42);
assert(!L.empty());
push |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
Returns a new list with x added to the front. The original list
is unchanged.
par::list<int> L;
auto L1 = L.push(3);
auto L2 = L1.push(2);
// L == []
// L1 == [3]
// L2 == [2, 3]
pop |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
Returns the first element and the remainder of the list.
The list must not be empty, or pop() is undefined.
par::list<int> L;
L = L.push(3).push(2).push(1); // [1, 2, 3]
auto [x, tail] = L.pop();
// x == 1
// tail == [2, 3]
peek |
Work | Span |
|---|---|---|
|
$O(1)$ | $O(1)$ |
Returns the first element of the list without removing it.
The list must not be empty, or peek() is undefined.
par::list<int> L;
L = L.push(10).push(20); // [20, 10]
int x = L.peek(); // x == 20
length / size |
Work | Span |
|---|---|---|
|
$O(n)$ | $O(n)$ |
Returns the number of elements in the list.
par::list<int> L;
L = L.push(3).push(2).push(1);
std::size_t n = L.size(); // n == 3
std::size_t m = L.length(); // also 3
to_sequence |
Work | Span |
|---|---|---|
|
$O(n)$ | $O(n)$ |
Returns a par::array_sequence containing the elements of the list in order.
par::list<int> L;
L = L.push(3).push(2).push(1); // [1, 2, 3]
par::array_sequence<int> S = L.to_sequence(); // S == [1, 2, 3]
from_sequence |
Work | Span |
|---|---|---|
|
$O(|S|)$ | $O(|S|)$ |
Constructs a list containing the elements of S in the same order.
par::array_sequence<int> S{1, 2, 3};
auto L = par::list<int>::from_sequence(S); // L == [1, 2, 3]