Next: , Previous: Functions, Up: Programming


4.11 Arrays

Appending [] to a built-in or user-defined type yields an array. By default, attempts to access or assign to an array element using a negative index generates an error. While reading an array element with an index beyond the length of the array also generates an error, assignment to such an element causes the array to be resized to accommodate the new element. One can also index an array A with an integer array B, to obtain the array formed by indexing array A with successive elements of array B.

The declaration

real[] A;

initializes A to be an empty (zero-length) array. Empty arrays should be distinguished from null arrays. If we say

real[] A=null;

then A cannot be dereferenced at all (null arrays have no length and cannot be read from or assigned to).

Arrays can be explicitly initialized like this:

real[] A={0,1,2};

Array assignment in Asymptote does a shallow copy: only the pointer is copied (if one copy if modified, the other will be too). The copy function listed below provides a deep copy of an array.

Every array A of type T[] has the virtual members int length, void cyclic(bool), bool cyclicflag, T push(T), void append(T[]), and T pop(). The member A.length evaluates to the length of the array. Setting A.cyclic(true) signifies that array indices should be reduced modulo the current array length. Reading from or writing to a nonempty cyclic array never leads to out-of-bounds errors or array resizing. The member A.cyclicflag returns the current setting of the cyclic flag. The functions A.push and A.append append their arguments onto the end of the array (for convenience A.push also returns its argument), while A.pop() pops and returns the last element. Like all Asymptote functions, cyclic, push, pop, and append can be "pulled off" of the array and used on their own. For example,

int[] A={1};
A.push(2);         // A now contains {1,2}.
A.append(A);       // A now contains {1,2,1,2}.
int f(int)=A.push;
f(3);              // A now contains {1,2,1,2,3}.
int g()=A.pop;
write(g());        // Outputs 3.

The [] suffix can also appear after the variable name; this is sometimes convenient for declaring a list of variables and arrays of the same type:

real a,A[];
This declares a to be real and implicitly declares A to be of type real[]. But beware that this alternative syntax does not construct certain internal type-dependent functions that take real[] as an argument: alias, copy, concat, sequence, map, and transpose for type real[] won't be defined until the type real[] is used explicitly somewhere.

In the following list of built-in array functions, T represents a generic type.

new T[]
returns a new empty array of type T[];


new T[] {list}
returns a new array of type T[] initialized with list (a comma delimited list of elements).
new T[n]
returns a new array of n elements of type T[]. Unless they are arrays themselves, these n array elements are not initialized.


int[] sequence(int n)
if n >= 1 returns the array {0,1,...,n-1} (otherwise returns a null array);
int[] sequence(int n, int m)
if m >= n returns an array {n,n+1,...,m} (otherwise returns a null array);
T[] sequence(T f(int),n)
if n >= 1 returns the sequence {f_i :i=0,1,...n-1} given a function T f(int) and integer int n (otherwise returns a null array);


int[] reverse(int n)
if n >= 1 returns the array {n-1,n-2,...,0} (otherwise returns a null array);


int find(bool[], int n=1)
returns the index of the nth true value or -1 if not found. If n is negative, search backwards from the end of the array for the -nth value;


int search(T[], T key)
For ordered types T, searches a sorted ordered array of n elements to find an interval containing key, returning -1 if key is less than the first element, n-1 if key is greater than or equal to the last element, and otherwise the index corresponding to the left-hand (smaller) endpoint.


T[] copy(T[] A)
returns a deep copy of the array A;


T[] concat(T[] A, T[] B)
returns a new array formed by concatenating arrays A and B;


bool alias(T[] A, T[] B)
returns true if the arrays A and B are identical;


T[] sort(T[] A)
For ordered types T, returns a copy of A sorted in ascending order;
T[][] sort(T[][] A)
For ordered types T, returns a copy of A with the rows sorted by the first column, breaking ties with successively higher columns. For example:
     
     string[][] a={{"bob","9"},{"alice","5"},{"pete","7"},
                   {"alice","4"}};
     write("Row sort (by column 0, using column 1 to break ties):");
     write(stdout,sort(a));

produces

     
     alice   4
     alice   5
     bob     9
     pete    7


T[][] transpose(T[][] A)
returns the transpose of A.


T sum(T[] A)
For arithmetic types T, returns the sum of A.


T min(T[] A)
For ordered types T, returns the minimum element of A.


T max(T[] A)
For ordered types T, returns the maximum element of A.


map(f(T), T[] A)
returns the array obtained by applying the function f to each element of the array A.


T[] min(T[] A, T[] B)
For ordered types T, and arrays A and B of the same length, returns an array composed of the minimum of the corresponding elements of A and B.


T[] max(T[] A, T[] B)
For ordered types T, and arrays A and B of the same length, returns an array composed of the maximum of the corresponding elements of A and B.


pair[] fft(pair[] A, int sign)
returns the Fast Fourier Transform of A (if the optional FFTW package is installed), using the given sign. Here is a simple example:
     
     int n=4;
     pair[] f=sequence(n);
     write(f);
     pair[] g=fft(f,-1);
     write();
     write(g);
     f=fft(g,1);
     write();
     write(f/n);


real[] tridiagonal(real[] a, real[] b, real[] c, real[] f);
Solve the periodic tridiagonal problem L^-1 f, where f is an n vector and L is the n \times n matrix
     
     [ b[0] c[0]           a[0]   ]
     [ a[1] b[1] c[1]             ]
     [      a[2] b[2] c[2]        ]
     [                ...         ]
     [       c[n-1] a[n-1] b[n-1] ]
For Dirichlet boundary conditions (denoted here by u[-1] and u[n]), replace f[0] by f[0]-a[0]u[-1] and f[n-1]-c[n-1]u[n]; then set a[0]=c[n-1]=0.


real[] quadraticroots(real a, real b, real c);
This numerically robust solver returns the real roots of the quadratic equation ax^2+bx+c=0.


real[] cubicroots(real a, real b, real c, real d);
This numerically robust solver returns the real roots of the cubic equation ax^3+bx^2+cx+d=0.

Asymptote includes a full set of vectorized array instructions for arithmetic (including self) and logical operations. These element-by-element instructions are implemented in C++ code for speed. Given

real[] a={1,2};
real[] b={3,2};
then a == b and a >= 2 both evaluate to the vector {false, true}. To test whether all components of a and b agree, use the boolean function all(a == b). One can also use conditionals like (a >= 2) ? a : b, which returns the array {3,2}, or write((a >= 2) ? a : null, which returns the array {2}.

All of the standard built-in libm functions of signature real(real) also take a real array as an argument, effectively like an implicit call to map.

As with other built-in types, arrays of the basic data types can be read in by assignment. In this example, the code

file fin=input("test.txt");
real[] A=fin;

reads real values into A until the end of file is reached (or an I/O error occurs). If line mode is set with line(file), then reading will stop once the end of the line is reached instead (line mode may be cleared with line(file,false)):

file fin=input("test.txt");
real[] A=line(fin);

Another useful mode is comma-separated-value mode, set with csv(file) and cleared with csv(file,false), which skips over any comma delimiters:

file fin=input("test.txt");
real[] A=csv(fin);

To restrict the number of values read, use the dimension(file,int) function:

file fin=input("test.txt");
real[] A=dimension(fin,10);

This reads 10 values into A, unless end-of-file (or end-of-line in line mode) occurs first. Attempting to read beyond the end of the file will produce a runtime error message. Specifying a value of 0 for the integer limit is equivalent to the previous example of reading until end-of-file (or end-of-line in line mode) is encountered.

Two- and three-dimensional arrays of the basic data types can be read in like this:

file fin=input("test.txt");
real[][] A=dimension(fin,2,3);
real[][][] B=dimension(fin,2,3,4);
Again, an integer limit of zero means no restriction.

Sometimes the array dimensions are stored with the data as integer fields at the beginning of an array. Such arrays can be read in with the functions read1, read2, and read3, respectively:

file fin=input("test.txt");
real[] A=read1(fin);
real[][] B=read2(fin);
real[][][] C=read3(fin);

One, two, and three-dimensional arrays of the basic data types can be output with the functions write(file,T[]), write(file,T[][]), write(file,T[][][]), respectively. The command scroll(int n) is useful for pausing the output after every n output lines (press Enter to continue).