Lists

Lists are the most important way to treat objects together. A list arranges objects in a definite order. So each list implies a partial mapping from the integers to the elements of the list. I.e., there is a first element of a list, a second, a third, and so on. Lists can occur in mutable or immutable form, see for the concept of mutability, and for the case of lists.

This chapter deals mainly with the aspect of lists in &GAP; as data structures. Chapter tells more about the collection aspect of certain lists, and more about lists as arithmetic objects can be found in the chapters and .

Lists are used to implement ranges (see ), sets (see ),Sets strings (see ), row vectors (see ), and matrices (see ); Boolean lists (see ) are a further special kind of lists.

Several operations for lists, such as and , will be described in Chapter , in particular see .

List Categories

A list can be written by writing down the elements in order between square brackets [, ], and separating them with commas ,. An empty list, i.e., a list with no elements, is written as [].

gap> [ 1, 2, 3 ]; # a list with three elements [ 1, 2, 3 ] gap> [ [], [ 1 ], [ 1, 2 ] ]; # a list may contain other lists [ [ ], [ 1 ], [ 1, 2 ] ]

Each list constructed this way is mutable (see ).

<#Include Label="IsList"> <#Include Label="IsDenseList"> <#Include Label="IsHomogeneousList"> <#Include Label="IsTable"> <#Include Label="IsRectangularTable"> <#Include Label="IsConstantTimeAccessList">

Basic Operations for Lists

The basic operations for lists are element access (see ), assignment of elements to a list (see ), fetching the length of a list (see ), the test for a hole at a given position, and unbinding an element at a given position (see ).

The term basic operation means that each other list operation can be formulated in terms of the basic operations. (But note that usually a more efficient method than this one is implemented.)

Any &GAP; object list in the category is regarded as a list, and if methods for the basic list operations are installed for list then list can be used also for the other list operations.

For internally represented lists, kernel methods are provided for the basic list operations. For other lists, it is possible to install appropriate methods for these operations. This permits the implementation of lists that do not need to store all list elements (see also ); for example, the elements might be described by an algorithm, such as the elements list of a group. For this reduction of space requirements, however, a price in access time may have to be paid (see ).

list element
list boundedness test
list assignment
list unbind
These operations implement element access, test for element boundedness, list element assignment, and removal of the element at position pos. In all cases, the index pos must be a positive integer.

Note that the special characters [, ], :, and = must be escaped with a backslash \ (see ); so denotes the operation for element access in a list, whereas [] denotes an empty list. (Maybe the variable names involving special characters look strange, but nevertheless they are quite suggestive.)

\[\]( list, pos )
is equivalent to list[ pos ], which clearly will usually be preferred; the former is useful mainly if one wants to access the operation itself, for example if one wants to install a method for element access in a special kind of lists.

Similarly, is used explicitly mainly in method installations. In other situations, one can simply call , which then delegates to if the first argument is a list, and to if the first argument is a record.

Analogous statements hold for and .
List Elements
accessing
list element
list[ pos ]

The above construct evaluates to the pos-th element of the list list, where pos must be a positive integer. List indexing is done with origin 1, i.e., the first element of the list is the element at position 1. gap> l := [ 2, 3, 5, 7, 11, 13 ];; l[1]; l[2]; l[6]; 2 3 13 If list is not a list, or pos does not evaluate to a positive integer, or list[pos] is unbound an error is signalled.

sublist
sublist
list{ poss }

The above construct evaluates to a new list new whose first element is list[poss[1]], whose second element is list[poss[2]], and so on. poss must be a dense list of positive integers. However, it does not need to be sorted and may contain duplicate elements. If for any i, list[ poss[i] ] is unbound, an error is signalled.

gap> l := [ 2, 3, 5, 7, 11, 13, 17, 19 ];; gap> l{[4..6]}; l{[1,7,1,8]}; [ 7, 11, 13 ] [ 2, 17, 2, 19 ]

The result is a new list, that is not identical to any other list. The elements of that list, however, are identical to the corresponding elements of the left operand (see ).

It is possible to nest such sublist extractions, as can be seen in the example below.

gap> m := [ [1,2,3], [4,5,6], [7,8,9], [10,11,12] ];; m{[1,2,3]}{[3,2]}; [ [ 3, 2 ], [ 6, 5 ], [ 9, 8 ] ] gap> l := m{[1,2,3]};; l{[3,2]}; [ [ 7, 8, 9 ], [ 4, 5, 6 ] ]

Note the difference between the two examples. The latter extracts elements 1, 2, and 3 from m and then extracts the elements 3 and 2 from this list. The former extracts elements 1, 2, and 3 from m and then extracts the elements 3 and 2 from each of those element lists.

To be precise: With each selector [pos] or {poss} we associate a level that is defined as the number of selectors of the form {poss} to its left in the same expression. For example

l[pos1]{poss2}{poss3}[pos4]{poss5}[pos6] level 0 0 1 2 2 3

Then a selector list[pos] of level level is computed as ListElement(list,pos,level), where ListElement is defined as follows. (Note that ListElement is not a &GAP; function.)

ListElement := function ( list, pos, level ) if level = 0 then return list[pos]; else return List( list, elm -> ListElement(elm,pos,level-1) ); fi; end;

and a selector list{poss} of level level is computed as ListElements(list,poss,level), where ListElements is defined as follows. (Note that ListElements is not a &GAP; function.)

ListElements := function ( list, poss, level ) if level = 0 then return list{poss}; else return List( list, elm -> ListElements(elm,poss,level-1) ); fi; end;

sublist
This operation implements sublist access. For any list, the default method is to loop over the entries in the list poss, and to delegate to the element access operation. (For the somewhat strange variable name, cf. .)
List Assignment
assignment
list element
list[ pos ] := object;

The list element assignment assigns the object object, which can be of any type, to the list entry at the position pos, which must be a positive integer, in the mutable (see ) list list. That means that accessing the pos-th element of the list list will return object after this assignment.

gap> l := [ 1, 2, 3 ];; gap> l[1] := 3;; l; # assign a new object [ 3, 2, 3 ] gap> l[2] := [ 4, 5, 6 ];; l; # <object> may be of any type [ 3, [ 4, 5, 6 ], 3 ] gap> l[ l[1] ] := 10;; l; # <index> may be an expression [ 3, [ 4, 5, 6 ], 10 ]

If the index pos is larger than the length of the list list (see ), the list is automatically enlarged to make room for the new element. Note that it is possible to generate lists with holes that way.

gap> l[4] := "another entry";; l; # <list> is enlarged [ 3, [ 4, 5, 6 ], 10, "another entry" ] gap> l[ 10 ] := 1;; l; # now <list> has a hole [ 3, [ 4, 5, 6 ], 10, "another entry",,,,,, 1 ]

The function should be used if you want to add an element to the end of the list.

Note that assigning to a list changes the list, thus this list must be mutable (see ). See for subtleties of changing lists.

If list does not evaluate to a list, pos does not evaluate to a positive integer or object is a call to a function which does not return a value (for example Print) an error is signalled.

sublist
list{ poss } := objects;

The sublist assignment assigns the object objects[1], which can be of any type, to the list list at the position poss[1], the object objects[2] to list[poss[2]], and so on. poss must be a dense list of positive integers, it need, however, not be sorted and may contain duplicate elements. objects must be a dense list and must have the same length as poss.

gap> l := [ 2, 3, 5, 7, 11, 13, 17, 19 ];; gap> l{[1..4]} := [10..13];; l; [ 10, 11, 12, 13, 11, 13, 17, 19 ] gap> l{[1,7,1,10]} := [ 1, 2, 3, 4 ];; l; [ 3, 11, 12, 13, 11, 13, 2, 19,, 4 ]

The next example shows that it is possible to nest such sublist assignments.

gap> m := [ [1,2,3], [4,5,6], [7,8,9], [10,11,12] ];; gap> m{[1,2,3]}{[3,2]} := [ [11,12], [13,14], [15,16] ];; m; [ [ 1, 12, 11 ], [ 4, 14, 13 ], [ 7, 16, 15 ], [ 10, 11, 12 ] ]

The exact behaviour is defined in the same way as for list extractions (see ). Namely, with each selector [pos] or {poss} we associate a level that is defined as the number of selectors of the form {poss} to its left in the same expression. For example

l[pos1]{poss2}{poss3}[pos4]{poss5}[pos6] level 0 0 1 1 1 2

Then a list assignment list[pos] := vals; of level level is computed as ListAssignment( list, pos, vals, level ), where ListAssignment is defined as follows. (Note that ListAssignment is not a &GAP; function.)

ListAssignment := function ( list, pos, vals, level ) local i; if level = 0 then list[pos] := vals; else for i in [1..Length(list)] do ListAssignment( list[i], pos, vals[i], level-1 ); od; fi; end;

and a list assignment list{poss} := vals of level level is computed as ListAssignments( list, poss, vals, level ), where ListAssignments is defined as follows. (Note that ListAssignments is not a &GAP; function.)

ListAssignments := function ( list, poss, vals, level ) local i; if level = 0 then list{poss} := vals; else for i in [1..Length(list)] do ListAssignments( list[i], poss, vals[i], level-1 ); od; fi; end;

sublist assignment
This operation implements sublist assignment. For any list, the default method is to loop over the entries in the list poss, and to delegate to the element assignment operation. (For the somewhat strange variable name, cf. .)
<#Include Label="Add"> <#Include Label="Remove">

This function copies n elements from fromlst, starting at position fromind and incrementing the position by fromstep each time, into tolst starting at position toind and incrementing the position by tostep each time. fromlst and tolst must be plain lists. fromstep and/or tostep can be negative. Unbound positions of fromlst are simply copied to tolst.

is used in methods for the operations and .
<#Include Label="Append">

IsBound and Unbind for Lists
<#Include Label="IsBound_list"> <#Include Label="Unbind_list">

Identical Lists

With the list assignment (see ) it is possible to change a mutable list. This section describes the semantic consequences of this fact. (See also .)

First we define what it means when we say that an object is changed. You may think that in the following example the second assignment changes the integer.

i := 3; i := i + 1;

But in this example it is not the integer 3 which is changed, by adding one to it. Instead the variable i is changed by assigning the value of i+1, which happens to be 4, to i. The same thing happens in the example below.

l := [ 1, 2 ]; l := [ 1, 2, 3 ];

The second assignment does not change the first list, instead it assigns a new list to the variable l. On the other hand, in the following example the list is changed by the second assignment.

l := [ 1, 2 ]; l[3] := 3;

To understand the difference, think of a variable as a name for an object. The important point is that a list can have several names at the same time. An assignment var:= list; means in this interpretation that var is a name for the object list. At the end of the following example l2 still has the value [ 1, 2 ] as this list has not been changed and nothing else has been assigned to it.

l1 := [ 1, 2 ]; l2 := l1; l1 := [ 1, 2, 3 ];

But after the following example the list for which l2 is a name has been changed and thus the value of l2 is now [ 1, 2, 3 ].

l1 := [ 1, 2 ]; l2 := l1; l1[3] := 3;

We say that two lists are identical if changing one of them by a list assignment also changes the other one. This is slightly incorrect, because if two lists are identical, there are actually only two names for one list. However, the correct usage would be very awkward and would only add to the confusion. Note that two identical lists must be equal, because there is only one list with two different names. Thus identity is an equivalence relation that is a refinement of equality. Identity of objects can be detected using .

Let us now consider under which circumstances two lists are identical.

If you enter a list literal then the list denoted by this literal is a new list that is not identical to any other list. Thus in the following example l1 and l2 are not identical, though they are equal of course.

l1 := [ 1, 2 ]; l2 := [ 1, 2 ];

Also in the following example, no lists in the list l are identical.

l := []; for i in [1..10] do l[i] := [ 1, 2 ]; od;

If you assign a list to a variable no new list is created. Thus the list value of the variable on the left hand side and the list on the right hand side of the assignment are identical. So in the following example l1 and l2 are identical lists.

l1 := [ 1, 2 ]; l2 := l1;

If you pass a list as an argument, the old list and the argument of the function are identical. Also if you return a list from a function, the old list and the value of the function call are identical. So in the following example l1 and l2 are identical lists:

l1 := [ 1, 2 ]; f := function ( l ) return l; end; l2 := f( l1 );

If you change a list it keeps its identity. Thus if two lists are identical and you change one of them, you also change the other, and they are still identical afterwards. On the other hand, two lists that are not identical will never become identical if you change one of them. So in the following example both l1 and l2 are changed, and are still identical.

l1 := [ 1, 2 ]; l2 := l1; l1[1] := 2;
Duplication of Lists

Here we describe the meaning of and for lists. For the general definition of these functions, see .

ShallowCopy
The subobjects (see ) of a list are exactly its elements.

This means that for any list list, returns a mutable new list new that is not identical to any other list (see ), and whose elements are identical to the elements of list.

StructuralCopy
Analogously, for a mutable list list, returns a mutable new list scp that is not identical to any other list, and whose elements are structural copies (defined recursively) of the elements of list; an element of scp is mutable (and then a new list) if and only if the corresponding element of list is mutable.

In both cases, modifying the copy new resp. scp by assignments (see ) does not modify the original object list.

basically executes the following code for lists. new := []; for i in [ 1 .. Length( list ) ] do if IsBound( list[i] ) then new[i] := list[i]; fi; od;

gap> list1 := [ [ 1, 2 ], [ 3, 4 ] ];; list2 := ShallowCopy( list1 );; gap> IsIdenticalObj( list1, list2 ); false gap> IsIdenticalObj( list1[1], list2[1] ); true gap> list2[1] := 0;; list1; list2; [ [ 1, 2 ], [ 3, 4 ] ] [ 0, [ 3, 4 ] ]

basically executes the following code for lists. new := []; for i in [ 1 .. Length( list ) ] do if IsBound( list[i] ) then new[i] := StructuralCopy( list[i] ); fi; od;

gap> list1 := [ [ 1, 2 ], [ 3, 4 ] ];; list2 := StructuralCopy( list1 );; gap> IsIdenticalObj( list1, list2 ); false gap> IsIdenticalObj( list1[1], list2[1] ); false gap> list2[1][1] := 0;; list1; list2; [ [ 1, 2 ], [ 3, 4 ] ] [ [ 0, 2 ], [ 3, 4 ] ]

The above code is not entirely correct. If the object list contains a mutable object twice this object is not copied twice, as would happen with the above definition, but only once. This means that the copy new and the object list have exactly the same structure when viewed as a general graph.

gap> sub := [ 1, 2 ];; list1 := [ sub, sub ];; gap> list2 := StructuralCopy( list1 ); [ [ 1, 2 ], [ 1, 2 ] ] gap> list2[1][1] := 0;; list2; [ [ 0, 2 ], [ 0, 2 ] ] gap> list1; [ [ 1, 2 ], [ 1, 2 ] ]
Membership Test for Lists
in
element test
This function call or the infix variant obj in list tests whether there is a positive integer i such that list[i] = obj holds.

If the list list knows that it is strictly sorted (see ), the membership test is much quicker, because a binary search can be used instead of the linear search used for arbitrary lists, see .

gap> 1 in [ 2, 2, 1, 3 ]; 1 in [ 4, -1, 0, 3 ]; true false gap> s := SSortedList( [2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32] );; gap> 17 in s; # uses binary search and only 4 comparisons false

For finding the position of an element in a list, see .
Enlarging Internally Represented Lists

Section told you (among other things) that it is possible to assign beyond the logical end of a mutable list, automatically enlarging the list. This section tells you how this is done for internally represented lists.

It would be extremely wasteful to make all lists large enough so that there is room for all assignments, because some lists may have more than 100000 elements, while most lists have less than 10 elements.

On the other hand suppose every assignment beyond the end of a list would be done by allocating new space for the list and copying all entries to the new space. Then creating a list of 1000 elements by assigning them in order, would take half a million copy operations and also create a lot of garbage that the garbage collector would have to reclaim.

So the following strategy is used. If a list is created it is created with exactly the correct size. If a list is enlarged, because of an assignment beyond the end of the list, it is enlarged by at least length/8 + 4 entries. Therefore the next assignments beyond the end of the list do not need to enlarge the list. For example creating a list of 1000 elements by assigning them in order, would now take only 32 enlargements.

The result of this is of course that the physical length of a list may be larger than the logical length, which is usually called simply the length of the list. Aside from the implications for the performance you need not be aware of the physical length. In fact all you can ever observe, for example by calling , is the logical length.

Suppose that would have to take the physical length and then test how many entries at the end of a list are unassigned, to compute the logical length of the list. That would take too much time. In order to make , and other functions that need to know the logical length, more efficient, the length of a list is stored along with the list.

For fine tuning code dealing with plain lists we provide the following two functions.

a plain list
nothing
The function returns an empty plain list which has enough memory allocated for len entries. This can be useful for creating and filling a plain list with a known number of entries.

The function gives back to &GAP;'s memory manager the physical memory which is allocated for the plain list l but not needed by the current number of entries.

Note that there are similar functions and for strings instead of plain lists. gap> l:=[]; for i in [1..160] do Add(l, i^2); od; [ ] gap> m:=EmptyPlist(160); for i in [1..160] do Add(m, i^2); od; [ ] gap> # now l uses about 25% more memory than the equal list m gap> ShrinkAllocationPlist(l); gap> # now l and m use the same amount of memory
Comparisons of Lists
comparisons
list equal
list1 = list2

list1 <> list2

Two lists list1 and list2 are equal if and only if for every index i, either both entries list1[i] and list2[i] are unbound, or both are bound and are equal, i.e., list1[i] = list2[i] is true.

gap> [ 1, 2, 3 ] = [ 1, 2, 3 ]; true gap> [ , 2, 3 ] = [ 1, 2, ]; false gap> [ 1, 2, 3 ] = [ 3, 2, 1 ]; false

This definition will cause problems with lists which are their own entries. Comparing two such lists for equality may lead to an infinite recursion in the kernel if the list comparison has to compare the list entries which are in fact the lists themselves, and then &GAP; crashes.

list smaller
list1 < list2

list1 <= list2

Lists are ordered lexicographically. Unbound entries are smaller than any bound entry. That implies the following behaviour. Let i be the smallest positive integer i such that list1 and list2 at position i differ, i.e., either exactly one of list1[i], list2[i] is bound or both entries are bound and differ. Then list1 is less than list2 if either list1[i] is unbound (and list2[i] is not) or both are bound and list1[i] < list2[i] is true.

gap> [ 1, 2, 3, 4 ] < [ 1, 2, 4, 8 ]; # <list1>[3] < <list2>[3] true gap> [ 1, 2, 3 ] < [ 1, 2, 3, 5 ]; # <list1>[4] is unbound and thus < 5 true gap> [ 1, , 3, 4 ] < [ 1, -1, 3 ]; # <list1>[2] is unbound and thus < -1 true

Note that for comparing two lists with < or <=, the (relevant) list elements must be comparable with <, which is usually not the case for objects in different families, see . Also for the possibility to compare lists with other objects, see .

Arithmetic for Lists
operators

It is convenient to have arithmetic operations for lists, in particular because in &GAP; row vectors and matrices are special kinds of lists. However, it is the wide variety of list objects because of which we prescribe arithmetic operations not for all of them. (Keep in mind that list means just an object in the category .)

(Due to the intended generality and flexibility, the definitions given in the following sections are quite technical. But for not too complicated cases such as matrices (see ) and row vectors (see ) whose entries aren't lists, the resulting behaviour should be intuitive.)

For example, we want to deal with matrices which can be added and multiplied in the usual way, via the infix operators + and *; and we want also Lie matrices, with the same additive behaviour but with the multiplication defined by the Lie bracket. Both kinds of matrices shall be lists, with the usual access to their rows, with returning the number of rows etc.

For the categories and attributes that control the arithmetic behaviour of lists, see .

For the definition of return values of additive and multiplicative operations whose arguments are lists in these filters, see and , respectively. It should be emphasized that these sections describe only what the return values are, and not how they are computed.

For the mutability status of the return values, see . (Note that this is not dealt with in the sections about the result values.)

Further details about the special cases of row vectors and matrices can be found in and in , the compression status is dealt with in and .

Filters Controlling the Arithmetic Behaviour of Lists
<#Include Label="[1]{arith}"> <#Include Label="IsGeneralizedRowVector"> <#Include Label="IsMultiplicativeGeneralizedRowVector"> <#Include Label="IsListDefault"> <#Include Label="NestingDepthA"> <#Include Label="NestingDepthM">

Additive Arithmetic for Lists

In this general context, we define the results of additive operations only in the following situations. For unary operations (zero and additive inverse), the unique argument must be in ; for binary operations (addition and subtraction), at least one argument must be in , and the other either is not a list or also in .

(For non-list &GAP; objects, defining the results of unary operations is not an issue here, and if at least one argument is a list not in , it shall be left to this argument whether the result in question is defined and what it is.)

Zero for lists

The zero (see ) of a list x in is defined as the list whose entry at position i is the zero of x[i] if this entry is bound, and is unbound otherwise.

gap> Zero( [ 1, 2, 3 ] ); Zero( [ [ 1, 2 ], 3 ] ); Zero( liemat ); [ 0, 0, 0 ] [ [ 0, 0 ], 0 ] LieObject( [ [ 0, 0 ], [ 0, 0 ] ] )
AdditiveInverse for lists

The additive inverse (see ) of a list x in is defined as the list whose entry at position i is the additive inverse of x[i] if this entry is bound, and is unbound otherwise.

gap> AdditiveInverse( [ 1, 2, 3 ] ); AdditiveInverse( [ [ 1, 2 ], 3 ] ); [ -1, -2, -3 ] [ [ -1, -2 ], -3 ]
Addition of lists
addition
If x and y are in and have the same additive nesting depth (see ), the sum x + y is defined pointwise, in the sense that the result is a list whose entry at position i is x[i] + y[i] if these entries are bound, is a shallow copy (see ) of x[i] or y[i] if the other argument is not bound at position i, and is unbound if both x and y are unbound at position i.

If x is in and y is in and has lower additive nesting depth, or is neither a list nor a domain, the sum x + y is defined as a list whose entry at position i is x[i] + y if x is bound at position i, and is unbound if not. The equivalent holds in the reversed case, where the order of the summands is kept, as addition is not always commutative.

gap> 1 + [ 1, 2, 3 ]; [ 1, 2, 3 ] + [ 0, 2, 4 ]; [ 1, 2 ] + [ Z(2) ]; [ 2, 3, 4 ] [ 1, 4, 7 ] [ 0*Z(2), 2 ] gap> l1:= [ 1, , 3, 4 ];; l2:= [ , 2, 3, 4, 5 ];; gap> l3:= [ [ 1, 2 ], , [ 5, 6 ] ];; l4:= [ , [ 3, 4 ], [ 5, 6 ] ];; gap> NestingDepthA( l1 ); NestingDepthA( l2 ); 1 1 gap> NestingDepthA( l3 ); NestingDepthA( l4 ); 2 2 gap> l1 + l2; [ 1, 2, 6, 8, 5 ] gap> l1 + l3; [ [ 2, 2, 3, 4 ],, [ 6, 6, 3, 4 ] ] gap> l2 + l4; [ , [ 3, 6, 3, 4, 5 ], [ 5, 8, 3, 4, 5 ] ] gap> l3 + l4; [ [ 1, 2 ], [ 3, 4 ], [ 10, 12 ] ] gap> l1 + []; [ 1,, 3, 4 ]
Subtraction of lists
list and non-list
For two &GAP; objects x and y of which one is in and the other is also in or is neither a list nor a domain, x - y is defined as x + (-y).

gap> l1 - l2; [ 1, -2, 0, 0, -5 ] gap> l1 - l3; [ [ 0, -2, 3, 4 ],, [ -4, -6, 3, 4 ] ] gap> l2 - l4; [ , [ -3, -2, 3, 4, 5 ], [ -5, -4, 3, 4, 5 ] ] gap> l3 - l4; [ [ 1, 2 ], [ -3, -4 ], [ 0, 0 ] ] gap> l1 - []; [ 1,, 3, 4 ]
Multiplicative Arithmetic for Lists

In this general context, we define the results of multiplicative operations only in the following situations. For unary operations (one and inverse), the unique argument must be in ; for binary operations (multiplication and division), at least one argument must be in , and the other either not a list or also in .

(For non-list &GAP; objects, defining the results of unary operations is not an issue here, and if at least one argument is a list not in , it shall be left to this argument whether the result in question is defined and what it is.)

One for lists

The one (see ) of a dense list x in such that x has even multiplicative nesting depth and has the same length as each of its rows is defined as the usual identity matrix on the outer two levels, that is, an identity matrix of the same dimensions, with diagonal entries One( x[1][1] ) and off-diagonal entries Zero( x[1][1] ).

gap> One( [ [ 1, 2 ], [ 3, 4 ] ] ); [ [ 1, 0 ], [ 0, 1 ] ] gap> One( [ [ [ [ 1 ] ], [ [ 2 ] ] ], [ [ [ 3 ] ], [ [ 4 ] ] ] ] ); [ [ [ [ 1 ] ], [ [ 0 ] ] ], [ [ [ 0 ] ], [ [ 1 ] ] ] ]
Inverse for lists

The inverse (see ) of an invertible square table x in whose entries lie in a common field is defined as the usual inverse y, i.e., a square matrix over the same field such that x y and y x is equal to One( x ).

gap> Inverse( [ [ 1, 2 ], [ 3, 4 ] ] ); [ [ -2, 1 ], [ 3/2, -1/2 ] ]
Multiplication of lists
list and non-list
There are three possible computations that might be triggered by a multiplication involving a list in . Namely, x * y might be (I) the inner product x[1] * y[1] + x[2] * y[2] + \cdots + x[n] * y[n], where summands are omitted for which the entry in x or y is unbound (if this leaves no summand then the multiplication is an error), or (L) the left scalar multiple, i.e., a list whose entry at position i is x * y[i] if y is bound at position i, and is unbound if not, or (R) the right scalar multiple, i.e., a list whose entry at position i is x[i] * y if x is bound at position i, and is unbound if not.

Our aim is to generalize the basic arithmetic of simple row vectors and matrices, so we first summarize the situations that shall be covered.

scl
vec
mat
scl
(L)
(L)
vec
(R)
(I)
(I)
mat
(R)
(R)
(R)

This means for example that the product of a scalar (scl) with a vector (vec) or a matrix (mat) is computed according to (L). Note that this is asymmetric.

Now we can state the general multiplication rules.

If exactly one argument is in then we regard the other argument (which is then neither a list nor a domain) as a scalar, and specify result (L) or (R), depending on ordering.

In the remaining cases, both x and y are in , and we distinguish the possibilities by their multiplicative nesting depths. An argument with odd multiplicative nesting depth is regarded as a vector, and an argument with even multiplicative nesting depth is regarded as a scalar or a matrix.

So if both arguments have odd multiplicative nesting depth, we specify result (I).

If exactly one argument has odd nesting depth, the other is treated as a scalar if it has lower multiplicative nesting depth, and as a matrix otherwise. In the former case, we specify result (L) or (R), depending on ordering; in the latter case, we specify result (L) or (I), depending on ordering.

We are left with the case that each argument has even multiplicative nesting depth. If the two depths are equal, we treat the computation as a matrix product, and specify result (R). Otherwise, we treat the less deeply nested argument as a scalar and the other as a matrix, and specify result (L) or (R), depending on ordering.

gap> [ (), (2,3), (1,2), (1,2,3), (1,3,2), (1,3) ] * (1,4); [ (1,4), (1,4)(2,3), (1,2,4), (1,2,3,4), (1,3,2,4), (1,3,4) ] gap> [ 1, 2, , 4 ] * 2; [ 2, 4,, 8 ] gap> [ 1, 2, 3 ] * [ 1, 3, 5, 7 ]; 22 gap> m:= [ [ 1, 2 ], 3 ];; m * m; [ [ 7, 8 ], [ [ 3, 6 ], 9 ] ] gap> m * m = [ m[1] * m, m[2] * m ]; true gap> n:= [ 1, [ 2, 3 ] ];; n * n; 14 gap> n * n = n[1] * n[1] + n[2] * n[2]; true
Division of lists
list and non-list
For two &GAP; objects x and y of which one is in and the other is also in or is neither a list nor a domain, x / y is defined as x * y^{{-1}}.

gap> [ 1, 2, 3 ] / 2; [ 1, 2 ] / [ [ 1, 2 ], [ 3, 4 ] ]; [ 1/2, 1, 3/2 ] [ 1, 0 ]
mod for lists
list and non-list
mod
If x and y are in and have the same multiplicative nesting depth (see ), x mod y is defined pointwise, in the sense that the result is a list whose entry at position i is x[i] mod y[i] if these entries are bound, is a shallow copy (see ) of x[i] or y[i] if the other argument is not bound at position i, and is unbound if both x and y are unbound at position i.

If x is in and y is in and has lower multiplicative nesting depth or is neither a list nor a domain, x mod y is defined as a list whose entry at position i is x[i] mod y if x is bound at position i, and is unbound if not. The equivalent holds in the reversed case, where the order of the arguments is kept.

gap> 4711 mod [ 2, 3,, 5, 7 ]; [ 1, 1,, 1, 0 ] gap> [ 2, 3, 4, 5, 6 ] mod 3; [ 2, 0, 1, 2, 0 ] gap> [ 10, 12, 14, 16 ] mod [ 3, 5, 7 ]; [ 1, 2, 0, 16 ]
Left quotients of lists
list and non-list
For two &GAP; objects x and y of which one is in and the other is also in or is neither a list nor a domain, LeftQuotient( x, y ) is defined as x^{{-1}} * y.

gap> LeftQuotient( [ [ 1, 2 ], [ 3, 4 ] ], [ 1, 2 ] ); [ 0, 1/2 ]
Mutability Status and List Arithmetic

Many results of arithmetic operations, when applied to lists, are again lists, and it is of interest whether their entries are mutable or not (if applicable). Note that the mutability status of the result itself is already defined by the general rule for any result of an arithmetic operation, not only for lists (see ).

However, we do not define exactly the mutability status for each element on each level of a nested list returned by an arithmetic operation. (Of course it would be possible to define this recursively, but since the methods used are in general not recursive, in particular for efficient multiplication of compressed matrices, such a general definition would be a burden in these cases.) Instead we consider, for a list x in , the sequence x = x_1, x_2, \ldots x_n where x_{{i+1}} is the first bound entry in x_i if exists (that is, if x_i is a nonempty list), and n is the largest i such that x_i lies in . The immutability level of x is defined as infinity if x is immutable, and otherwise the number of x_i which are immutable. (So the immutability level of a mutable empty list is 0.)

Thus a fully mutable matrix has immutability level 0, and a mutable matrix with immutable first row has immutability level 1 (independent of the mutability of other rows).

The immutability level of the result of any of the binary operations discussed here is the minimum of the immutability levels of the arguments, provided that objects of the required mutability status exist in &GAP;.

Moreover, the results have a homogeneous mutability status, that is, if the first bound entry at nesting depth i is immutable (mutable) then all entries at nesting depth i are immutable (mutable, provided that a mutable version of this entry exists in &GAP;).

Thus the sum of two mutable matrices whose first rows are mutable is a matrix all of whose rows are mutable, and the product of two matrices whose first rows are immutable is a matrix all of whose rows are immutable, independent of the mutability status of the other rows of the arguments.

For example, the sum of a matrix (mutable or immutable, i.e., of immutability level one of 0, 1, or 2) and a mutable row vector (i.e., immutability level 0) is a fully mutable matrix. The product of two mutable row vectors of integers is an integer, and since &GAP; does not support mutable integers, the result is immutable.

For unary arithmetic operations, there are three operations available, an attribute that returns an immutable result (, , , ), an operation that returns a result that is mutable (, , , ), and an operation whose result has the same immutability level as the argument (, , , ). The last kind of operations is equivalent to the corresponding infix operations 0 * list, - list, list^0, and list^-1. (This holds not only for lists, see .)

gap> IsMutable( l1 ); IsMutable( 2 * Immutable( [ 1, 2, 3 ] ) ); true false gap> IsMutable( l2 ); IsMutable( l3 ); true true

An example motivating the mutability rule is the use of syntactic constructs such as obj * list and - list as an elegant and efficient way to create mutable lists needed for further manipulations from mutable lists. In particular one can construct a mutable zero vector of length n by 0 * [ 1 .. n ]. The latter can be done also using .

<#Include Label="ListWithIdenticalEntries">

Finding Positions in Lists
<#Include Label="Position"> <#Include Label="Positions"> <#Include Label="PositionCanonical"> <#Include Label="PositionNthOccurrence"> <#Include Label="PositionSorted"> <#Include Label="PositionSet"> <#Include Label="PositionProperty"> <#Include Label="PositionsProperty"> <#Include Label="PositionBound"> <#Include Label="PositionNot"> <#Include Label="PositionNonZero"> <#Include Label="PositionSublist"> <#Include Label="PositionFirstComponent">

Properties and Attributes for Lists

A list that contains mutable objects (like lists or records) cannot store attribute values that depend on the values of its entries, such as whether it is homogeneous, sorted, or strictly sorted, as changes in any of its entries could change such property values, like the following example shows.

gap> l:=[[1],[2]]; [ [ 1 ], [ 2 ] ] gap> IsSSortedList(l); true gap> l[1][1]:=3; 3 gap> IsSSortedList(l); false

For such lists these property values must be computed anew each time the property is asked for. For example, if list is a list of mutable row vectors then the call of with list as first argument cannot take advantage of the fact that list is in fact sorted. One solution is to call explicitly in such a situation, another solution is to replace list by an immutable copy using .

<#Include Label="IsMatchingSublist"> <#Include Label="IsDuplicateFree"> <#Include Label="IsSortedList"> <#Include Label="IsSSortedList"> <#Include Label="Length"> <#Include Label="ConstantTimeAccessList">

Sorting Lists
<#Include Label="Sort"> <#Include Label="SortParallel"> <#Include Label="Sortex"> <#Include Label="SortingPerm">

The default methods for all of these sorting operations currently use Shell sort as it has a comparable performance to Quicksort for lists of length at most a few thousands, and has better worst-case behaviour.

Sorted Lists and Sets
sets
multisets

Searching objects in a list works much quicker if the list is known to be sorted. Currently &GAP; exploits the sortedness of a list automatically only if the list is strictly sorted, which is indicated by the property .

Remember that a list of mutable objects cannot store that it is strictly sorted but has to test it anew whenever it is asked whether it is sorted, see the remark in . Therefore &GAP; cannot take advantage of the sortedness of a list if this list has mutable entries. Moreover, if a sorted list list with mutable elements is used as an argument of a function that expects this argument to be sorted, for example or , then it is checked whether list is in fact sorted; this check can have the effect actually to slow down the computations, compared to computations with sorted lists of immutable elements or computations that do not involve functions that do automatically check sortedness.

Strictly sorted lists are used to represent sets in &GAP;. More precisely, a strictly sorted list is called a proper set in the following, in order to avoid confusion with domains (see ) which also represent sets.

In short proper sets are represented by sorted lists without holes and duplicates in &GAP;. Note that we guarantee this representation, so you may make use of the fact that a set is represented by a sorted list in your functions.

In some contexts (for example see ), we also want to talk about multisets. A multiset is like a set, except that an element may appear several times in a multiset. Such multisets are represented by sorted lists without holes that may have duplicates.

This section lists only those functions that are defined exclusively for proper sets. Set theoretic functions for general collections, such as and , are described in Chapter . In particular, for the construction of proper sets, see and . For finding positions in sorted lists, see .

There are nondestructive counterparts of the functions , , and available for proper sets. These are UnionSet, IntersectionSet, and . The former two are methods for the more general operations and , the latter is itself an operation (see ).

The result of IntersectionSet and UnionSet is always a new list, that is not identical to any other list. The elements of that list however are identical to the corresponding elements of the first argument set. If set is not a proper set it is not specified to which of a number of equal elements in set the element in the result is identical (see ).

<#Include Label="[1]{set}">

For a list list that stores that it is strictly sorted, the test with whether the object obj is an entry of list uses binary search. This test can be entered also with the infix notation obj in list.
<#Include Label="IsEqualSet"> <#Include Label="IsSubsetSet"> <#Include Label="AddSet"> <#Include Label="RemoveSet"> <#Include Label="UniteSet"> <#Include Label="IntersectSet"> <#Include Label="SubtractSet">

Operations for Lists

Several of the following functions expect the first argument to be either a list or a collection (see ), with possibly slightly different meaning for lists and non-list collections.

concatenation
<#Include Label="Concatenation"> <#Include Label="Compacted"> <#Include Label="Collected"> <#Include Label="DuplicateFreeList"> <#Include Label="AsDuplicateFreeList"> <#Include Label="Flat"> <#Include Label="Reversed"> <#Include Label="Shuffle"> <#Include Label="IsLexicographicallyLess"> <#Include Label="Apply"> <#Include Label="Perform"> <#Include Label="PermListList"> <#Include Label="Maximum"> <#Include Label="Minimum"> <#Include Label="MaximumList"> <#Include Label="Cartesian"> <#Include Label="IteratorOfCartesianProduct"> <#Include Label="Permuted"> <#Include Label="List:list"> <#Include Label="Filtered"> <#Include Label="Number"> <#Include Label="First"> <#Include Label="ForAll"> <#Include Label="ForAny"> <#Include Label="Product"> <#Include Label="Sum"> <#Include Label="Iterated"> <#Include Label="ListN">

Advanced List Manipulations

The following functions are generalizations of , , , and .

<#Include Label="ListX"> <#Include Label="SetX"> <#Include Label="SumX"> <#Include Label="ProductX">

Ranges
range
A range is a dense list of integers in arithmetic progression (or degression). This is a list of integers such that the difference between consecutive elements is a nonzero constant. Ranges can be abbreviated with the syntactic construct

[ first, second .. last ]

or, if the difference between consecutive elements is 1, as

[ first .. last ]
.

If first > last, [ first .. last ] is the empty list, which by definition is also a range; also, if second > first > last or second < first < last, then [ first, second .. last ] is the empty list. If first = last, [ first, second .. last ] is a singleton list, which is a range, too. Note that last - first must be divisible by the increment second - first, otherwise an error is signalled.

Currently, the integers first, second and last and the length of a range must be small integers, that is at least -2^d and at most 2^d - 1 with d = 28 on 32-bit architectures and d = 60 on 64-bit architectures.

Note also that a range is just a special case of a list. Thus you can access elements in a range (see ), test for membership etc. You can even assign to such a range if it is mutable (see ). Of course, unless you assign last + second - first to the entry range[ Length( range ) + 1 ], the resulting list will no longer be a range.

gap> r := [10..20]; [ 10 .. 20 ] gap> Length( r ); 11 gap> r[3]; 12 gap> 17 in r; true gap> r[12] := 25;; r; # r is no longer a range [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25 ] gap> r := [1,3..17]; [ 1, 3 .. 17 ] gap> Length( r ); 9 gap> r[4]; 7 gap> r := [0,-1..-9]; [ 0, -1 .. -9 ] gap> r[5]; -4 gap> r := [ 1, 4 .. 32 ]; Error, Range: <last>-<first> (31) must be divisible by <inc> (3)

Most often ranges are used in connection with the for-loop see ). Here the construct

for var in [ first .. last ] do statements od

replaces the

for var from first to last do statements od

which is more usual in other programming languages.

gap> s := [];; for i in [10..20] do Add( s, i^2 ); od; s; [ 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400 ]

Note that a range with last >= first is at the same time also a proper set (see ), because it contains no holes or duplicates and is sorted, and also a row vector (see ), because it contains no holes and all elements are integers.

<#Include Label="IsRange"> <#Include Label="ConvertToRangeRep">

Enumerators

An enumerator is an immutable list that need not store its elements explicitly but knows, from a set of basic data, how to determine the i-th element and the position of a given object. A typical example of this is a vector space over a finite field with q elements, say, for which it is very easy to enumerate all elements using q-adic expansions of integers.

Using this enumeration can be even quicker than a binary search in a sorted list of vectors, see .

On the one hand, element access to an enumerator may take more time than element access to an internally represented list containing the same elements. On the other hand, an enumerator may save a vast amount of memory. Take for example a permutation group of size a few millions. Even for moderate degree it is unlikely that a list of all its elements will fit into memory whereas it is no problem to construct an enumerator from a stabilizer chain (see ).

There are situations where one only wants to loop over the elements of a domain, without using the special facilities of an enumerator, namely the particular order of elements and the possibility to find the position of elements. For such cases, &GAP; provides iterators (see ).

The functions and return enumerators of domains. Most of the special implementations of enumerators in the &GAP; library are based on the general interface that is provided by ; one generic example is , which can be used to get an enumerator of a finite dimensional free module.

Also enumerators for non-domains can be implemented via ; for a discussion, see .

<#Include Label="IsQuickPositionList">