FISH supports one-dimensional dynamic sized value based lists.

Lists are one-dimensional arrays of general FISH values. They differ from matrices in that each index can hold any valid FISH type (including another list). They differ from arrays in that they are passed by value, and they can only be of one dimension. They differ from maps in that they can be indexed only by consecutive integers starting at 1, but such indexing is more efficient.

While any iterable type can be the argument of a splitting operation (see Splitting), the return value of a split call will always be a list.

You can iterate through all the elements of a list using the loop foreach statement.

Lists are a handy way to pass and return multiple values using one symbol. Arbitrarily complex data structures can be created by embedding lists within lists.

Components of a list can be accessed using parentheses (). A single index can be used to get or set a single element of the list. As a special case, the string arguments ‘start’ and ‘end’ can be used (single argument only) to prepend or append to a list. The abbreviations ‘s’ and ‘e’ can be used as well.

Two indices can be used to get a sub-range of the list, or to update or resize a sub-range of a list on assignment. The return value is always a list (although it may be an empty list), and values assigned must be a list. The list assigned to a sub-range does not have to be the same length, the previous elements are replaced with the new elements and this may make the list longer or shorter. Using a two argument range for assignment, and assigning an empty list, removes that range from the list.

List can also be indexed using another list as a single argument – this is only available on get, it cannot currently be used for assignment. In this case, the list used as the argument must be composed entirely of positive integers (indices) or boolean values. In the case of boolean values, the return value is a list composed only of those elements that had true in the argument list. In the case of index values, the return value is a list of the elements in the original list at those indices.

The following example illustrates accessing list components using parentheses:

    local a = list.seq(1,'a',3.1,4,'b',math.pi) ;; Makes a 6 element list
    local b = a(2)          ;; b = 'a'
    a(2) = 'c'              ;; a = list.seq(1,'c',3.1,4,'b',math.pi)

    local c = a(3,4)        ;; c = list.seq(3.1,4)
    a(3,4) = list.seq('d')  ;; a = list.seq(1,'c','d','b',math.pi)
    a(3) = c                ;; Note: Now the third element of a is a list
    a(3,3) = c              ;; Note: In this case the 1 element range has
                            ;;        been replaced by the two element list
                            ;;        c, making the list longer
    a(2,2) = list           ;; Note: This removes the second element.

    c('start') = 1          ;; the value 1 has been prepended to c
    c('end')   = 4          ;; the value 4 has been appended to c

    local d = a(list.seq(false,true,false,false,true,false))
                            ;; d = list.seq('a','b')
    local e = a(list.seq(2,5))
                            ;; e = list.seq('a','b')

Binary and unary operators function differently on list types. Instead of operating on the lists themselves, the operators apply automatically to each element in the respective lists. If a list and a non-list type are involved in a binary operation, then the non-list type and operator is applied to each element of the list separately, and the result is a new list. If both sides are lists, then the operator is applied to each element of both lists separately in order, and the result is a third list. These operations may happen on multiple threads, if possible.

    local a = list.range(1,10)
    local b = list.range(20,11,-1)

    local d = a * 2  ;; The result is the same as list.range(2,20)
    local e = a + b  ;; The result is a 10 element list with all values of 21

The access operator -> also behaves differently when applied to lists. The retrieval/assignment is done on each element individually, and the result is returned as another list. If any element in the list does not support that accessor, then an error will occur. This applies to any type that is compatible with accessors - which at this time includes vectors, tensors, and structures. The following is a brief example of that syntax:

    ;; Create a list of vectors, as an example.
    local a = list.sequence( (1,2,3) , ...
                             (4,5,6) , ...
                             (7,8,9) , ...
                             (10,11,12) )
    local b = a->y ;; b is now a list of the y-components of the vectors in a,
                   ;;    or the list { 2, 5, 8, 11 }
    a->z /= 2      ;; the z components of the vectors in a are cut in half.

List types are created using the list or list.create library functions. A sequence of values can be created using the list.sequence function. A sequential range of numbers (from a start to an end using a given interval) can be created using the list.range function.

A number of additional methods, detailed in the List Utilities section, are available for lists.

Lists are an iterable type. This means you can use it as the target of a loop foreach statement (see Loop ForEach and can be split (see Splitting). Each entry of the list is iterated through in order.

As with most aggregate types, when the value is listed it will simply indicate that it is of type List and the the list size. To see the contents of the list use the fish list contents command.

Appending to a list

There are four different methods of appending to a list.

The first is to use the fish.append function, which returns a list with one or more values added to the end of the old one.

    ; {1,2,3,4,5,6,7,8,9,10}
    global l2 = list.append(l1,11)
    ; {1,2,3,4,5,6,7,8,9,10,11}

Note that if the value appended is itself another list, the new list returned will have one new value that is a list.

You can also append a single value to a list by using the string ‘end’ or ‘e’ argument on assignment. This method has a performance advantage in that a copy of the list is not (necessarily) made.

    ; {1,2,3,4,5,6,7,8,9,10}
    l3('e') = 11
    ; {1,2,3,4,5,6,7,8,9,10,11}

You can append the contents of another list (or any iterable type) to the end of a list by using the list.extend function.

    ; {1,2,3,4,5,6,7,8,9,10}
    l2 = list.extend(l1,list.range(11,13))
    ; {1,2,3,4,5,6,7,8,9,10,11,12,13}

This can also be done using two ‘e’ arguments to indicate a ranged insertion at the end:

    ; {1,2,3,4,5,6,7,8,9,10}
    l3('e','e') = list.sequence(11,12,13,14)
    ; {1,2,3,4,5,6,7,8,9,10,11,12,13,14}

Prepending to a list

Similarly, you can prepend a single value to a list using the list.prepend function.

    ; {1,2,3,4,5,6,7,8,9,10}
    l2 = list.prepend(0,l1)
    ; {0,1,2,3,4,5,6,7,8,9,10}

You can also use the ‘s’ argument to prepend a value:

    ; {1,2,3,4,5,6,7,8,9,10,11}
    l3('s') = 0
    ; {0,1,2,3,4,5,6,7,8,9,10,11}

Lastly you can use two ‘s’ arguments to prepend a range of values:

    ; {1,2,3,4,5,6,7,8,9,10}
    l3('s','s') = list.sequence(-3,-2,-1,0)
    ; {-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10}

Inserting into a list

You can insert one or more values into a list using the list.insert function.

    ; {1,2,3,4,5,6,7,8,9,10}
    l2 = list.insert(l1,3,'a')
    ; {1,2,'a',3,4,5,6,7,8,9,10}

You can also use two arguments to do a ranged insert. If the second argument is less than the first then the values appear inserted before the first. This is inserting into a null range.

One way to think of ranged (two argument) assignments to a list is the the values assigned fall from the start of the first argument to the end of the second.

    ; {1,2,3,4,5,6,7,8,9,10}
    l3(4,3) = list.sequence(-4,-3)
    ; {1,2,3,-4,-3,4,5,6,7,8,9,10}

Finally you can insert all elements in another list (or any iterable type) by using the list.insert.list function:

    ; {1,2,3,4,5,6,7,8,9,10}
    l2 = list.insert.list(l1,3,list.sequence('a','b','c'))
    ; {1,2,'a','b','c',3,4,5,6,7,8,9,10}