ligo/gitlab-pages/docs/reference/list.md

7.9 KiB

id title
list-reference List — Linear Collections

Lists are linear collections of elements of the same type. Linear means that, in order to reach an element in a list, we must visit all the elements before (sequential access). Elements can be repeated, as only their order in the collection matters. The first element is called the head, and the sub-list after the head is called the tail. For those familiar with algorithmic data structure, you can think of a list a stack, where the top is written on the left.

Defining Lists

const empty_list : list (int) = nil // Or list []
const my_list : list (int) = list [1; 2; 2] // The head is 1
let empty_list : int list = []
let my_list : int list = [1; 2; 2] // The head is 1
let empty_list : list (int) = [];
let my_list : list (int) = [1, 2, 2]; // The head is 1

Adding to Lists

Lists can be augmented by adding an element before the head (or, in terms of stack, by pushing an element on top).

const larger_list : list (int) = 5 # my_list // [5;1;2;2]
let larger_list : int list = 5 :: my_list // [5;1;2;2]
let larger_list : list (int) = [5, ...my_list]; // [5,1,2,2]

Functional Iteration over Lists

A functional iterator is a function that traverses a data structure and calls in turn a given function over the elements of that structure to compute some value. Another approach is possible in PascaLIGO: loops (see the relevant section).

There are three kinds of functional iterations over LIGO lists: the iterated operation, the map operation (not to be confused with the map data structure) and the fold operation.

Iterated Operation over Lists

The first, the iterated operation, is an iteration over the list with a unit return value. It is useful to enforce certain invariants on the element of a list, or fail.

function iter_op (const l : list (int)) : unit is
  block {
    function iterated (const i : int) : unit is
      if i > 2 then Unit else (failwith ("Below range.") : unit)
  } with list_iter (iterated, l)
let iter_op (l : int list) : unit =
  let predicate = fun (i : int) -> assert (i > 3)
  in List.iter predicate l
let iter_op = (l : list (int)) : unit => {
  let predicate = (i : int) => assert (i > 3);
  List.iter (predicate, l);
};

Mapped Operation over Lists

We may want to change all the elements of a given list by applying to them a function. This is called a map operation, not to be confused with the map data structure.

function increment (const i : int): int is i + 1

// Creates a new list with all elements incremented by 1
const plus_one : list (int) = list_map (increment, larger_list)
let increment (i : int) : int = i + 1

// Creates a new list with all elements incremented by 1
let plus_one : int list = List.map increment larger_list
let increment = (i : int) : int => i + 1;

// Creates a new list with all elements incremented by 1
let plus_one : list (int) = List.map (increment, larger_list);

Folded Operation over Lists

A folded operation is the most general of iterations. The folded function takes two arguments: an accumulator and the structure element at hand, with which it then produces a new accumulator. This enables having a partial result that becomes complete when the traversal of the data structure is over.

function sum (const acc : int; const i : int): int is acc + i
const sum_of_elements : int = list_fold (sum, my_list, 0)
let sum (acc, i: int * int) : int = acc + i
let sum_of_elements : int = List.fold sum my_list 0
let sum = ((result, i): (int, int)): int => result + i;
let sum_of_elements : int = List.fold (sum, my_list, 0);

List Length

Get the number of elements in a list.

function size_of (const l : list (int)) : nat is List.length (l)
let size_of (l : int list) : nat = List.length l
let size_of = (l : list (int)) : nat => List.length (l);

List.map(map_function: a' -> b', lst: a' list) : 'b list

Apply an operation defined by map_function to each element of a list and return a list of the modified elements.

function increment(const i: int): int is i + 1;
// Creates a new list with elements incremented by 1
const incremented_list: list(int) = list_map(increment, list 1; 2; 3; end );
let increment (i: int) : int = i + 1
(* Creates a new list with elements incremented by 1 *)
let incremented_list: int list = List.map increment [1; 2; 3]
let increment = (i: int): int => i + 1;
(* Creates a new list with elements incremented by 1 *)
let incremented_list: list(int) = List.map(increment, [1, 2, 3]);

List.iter(iter_function: a' -> unit, lst: a' list) : unit

Apply a side effecting function iter_function to each element of a list with no return value. This is useful for asserting that each element of a list satisfies a particular property.

function iter_op (const s : list(int)) : int is
  begin
    var r : int := 0 ;
    function aggregate (const i : int) : unit is
      begin
        r := r + i ;
      end with unit ;
    list_iter(aggregate, s) ;
  end with r
let iter_op (s : int list) : unit =
  let do_nothing = fun (_: int) -> unit
  in List.iter do_nothing s
let iter_op = (s: list(int)): unit => {
  let do_nothing = (z: int) => unit;
  List.iter(do_nothing, s);
};

List.fold(fold_function: (a' * a') -> a', lst: a' list, acc: a') : 'a

Combine the elements of a list into one value using the operation defined by fold_function'. For example, you could define summation by folding a list of integers. Starting with some initial accumulator value acc`, the fold:

  1. Consumes an element of the list.
  2. Passes the accumulator value to fold_function along with the element to produce a new accumulated value.
  3. The new accumulated value replaces the previous one.
  4. IF there are still elements in the list go back to 1, ELSE return the accumulator

Summation would be defined then by using a fold_function that takes two integers and adds them together. Each step of the fold would consume an element from the list and add it to the total until you've summed over the list.

function sum(const result: int; const i: int): int is result + i;
const sum_of_a_list: int = list_fold(sum, list 1; 2; 3; end, 0);
let sum (result, i: int * int) : int = result + i
let sum_of_a_list: int = List.fold sum [1; 2; 3] 0
let sum = ((result, i): (int, int)): int => result + i;
let sum_of_a_list: int = List.fold(sum, [1, 2, 3], 0);