BasicV a Dabhand Guide Part 3
Chapter 7 : Matrices
BASIC V adds significant matrix handling facilities to BBC BASIC. These not only result in much shorter and simpler coding when arrays or matrices are to be manipulated in any way, but are significantly faster in operation than equivalent for...next loops. For example, initialising all the elements of a three-dimensional array is some 70 times faster in BASIC V by this method. This is indicative of how much time is spent by BASIC in repeatedly interpreting for...next instructions rather than doing useful work.
Before investigating all this new power, let's start by clarifying the use of the terms array and matrix. An array is essentially a convenient method of storing a set of data items which involves giving a name to the set as a whole and referencing each data item by its position within the set. Arrays are stored as simple lists, or as two-dimensional arrays consisting of rows and columns, or as arrays of more than two dimensions. In BASIC V, arrays may be declared to store integer or floating point numbers, or strings.
Matrices are a mathematical concept derived from a study of algebra, and are often associated with such tasks as the solution of linear simultaneous equations. A matrix is normally two-dimensional consisting of rows and columns, but may also consist of a single row or a single column. Within BASIC v, matrices are, therefore, most conveniently stored as arrays, and effectively the whole-array operations may then be used to manipulate matrices.
It is not the purpose of this book to discuss matrix operations per se, or their application, but some explanation will be included as we proceed. Matrices are essentially concerned with numbers, and in BASIC are represented by integer or floating point arrays as appropriate. However, where it makes sense, matrix operations may also be applied to string arrays. For example, adding two string arrays together will result in a new array in which each string element consists of the two original string elements, in the same positions, added or concatenated together.
When a matrix operation involves two matrices, then there are usually constraints on the numbers of rows and columns in them. In most cases, both matrices must have the same number of rows and columns, though this is not always true, and other constraints sometimes apply. These will be dealt with in relation to each matrix operation.
Furthermore, these constraints apply to the size of an array as dimensioned, and not to the number of elements currently in use. For example, although BASIC always dimensions arrays with the first element in position zero, many programmers choose to ignore this and assume that the first element is in position one. Similarly, in a general application, an array may be dimensioned for the largest case likely to arise, but in practice often holds less data, and is processed accordingly. BASIC applies matrix operation to all elements, but no problems should arise if less than all elements are in use, provided this is applied consistently. Don't ignore the zero elements in one array, use them in another, and expect a matrix operation to give a sensible answer every time!
Introducing Whole-Array Operations
In whole-array operations, arrays are referenced by giving the name followed by the opening and closing parentheses thus:
Total () Name$() Freq%()
The dim statement, as well as being used to dimension arrays, can also be used to provide information about an array previously dimensioned. By specifying the name of an array, dim will return the number of dimensions with which the array was dimensioned, while specifying the name and a dimension will give the size of that dimension. For example, if an array were dimensioned as:
would return the value '2';
would return the value '20', and:
would return the value '50'.
Used in this way, dim returns a numeric value from 1 upwards, and may be used in any situation where a numeric variable or value is acceptable. The information supplied by this use of dim is very useful if it is necessary to apply checks to matrices prior to executing any matrix operation. Perhaps its greatest benefit is in procedures and functions which may now specify arrays as parameters. A procedure definition could use dim as described above to determine both the number of dimensions and the size of each dimension of any array passed to it. This provides valuable flexibility when writing functions and procedures to manipulate arrays passed as parameters/ already covered in some detail in Chapter Five.
Whole-Array Operations: Constants
Although arrays are always initialised to zero, or to null strings, it may be necessary to assign a different value to every element in an array. This is very simple, for example:
data()=1 bitmap%()=FALSE data$()=STRING$(30,CHR$32)
Under RISC OS it is also possible to assign different values to each element of an array by giving a list of suitable values or expressions. Thus:
Similarly, all the elements of an array may be incremented or decremented using the new '+=' or '-=' operators, as in:
The syntax here can be confusing. Where a constant value is to be assigned to all the elements of an array, it is essential that this be enclosed in parentheses if it is an expression rather than as a single value or variable. However, when incrementing or decrementing the elements of an array, parentheses are not essential. For example:
data ()=(inc+l) data()+=(inc+1) data()+=inc+l
are all acceptable, but:
is not. More confusingly, in the latter case the error message generated is "Type mismatch: array needed". Being aware of this will save endless frustration later. You can also copy all the elements from one array to another and negate all the elements of an array. For example:
New_Data()=01d_Data() or map%()=-map%()
but you cannot logically negate all the elements using NOT.
Arrays: Operation on with a Constant
The next array operations to consider are those where each element of an array is to be operated on with a constant. The operations available are addition, subtraction, multiplication and division. The resulting array may then be assigned to a new array, or used to replace the elements of the original one. We can also operate on a constant with an array element, or vice versa. With addition and multiplication (both commutative) the order makes no difference to the result, but clearly does so with subtraction and division. Remember, each and every element will be treated in exactly the same way. Examples are:
data()=data()+inc data$()=data$ ()+", " NetPrice()=GrossPrice()*1.15 Reciprocal()"I/Integer()
Note the string example - any matrix operation may be used on string arrays if this makes sense. In all the above examples, where two or more arrays are involved, they must all be dimensioned the same way (ie, the same number of dimensions, and the same size for each dimension). This applies in all other examples as well, except for matrix multiplication.
Binary Operations on Arrays
The same operations of addition, subtraction, multiplication and division may be used on the corresponding elements of two arrays. Thus we could, for example, write:
Totals()=datal()+data2() Profit()-Income()-Cost() Value()"Amount()*Exchange_Rate() Record$()=Record$()+New_Field$()
All these operations, including that of multiplication as specified by the '*' operator, work on corresponding pairs of elements. The resulting 'values' may then be assigned to a new array or used to replace the elements of either original array.
In addition, BASIC v also provides matrix multiplication using the '.' operator. This is quite different to the multiplication of corresponding elements as mentioned above. This mathematical operation can only be applied to two-dimensional arrays. It works as follows. The first row of the first matrix is used with the first column of the second matrix. A new element is then formed from the sum of the products of the corresponding pairs of elements. This is repeated for each row of the first matrix and each column of the second. For example:
since 14 = (3*2 + 4*2) 9 = (3*-1 + 4*3) 0 = (1*2 + -1*2) -4 = (1*-1 + -1*3)
Because of the way in which matrix multiplication is definied, the number of columns of the first matrix must correspond to the number of rows of the second. Furthermore, the array to which the results of a matrix multiplication are assigned must have the same number of rows as the first matrix, and the same number of columns as the second. For any three numbers i, j, and k, the operation of matrix multiplication of the form:
C() = A() . B()
is only valid if the three arrays A, B, and C have been dimensioned as:
DIM A(i,j),B (j,k),C(i,k)
Note that for a two-dimensional array, the first subscript is the row number and the second subscript the column number. The dim statement may be used as indicated before to perform the necessary checks. For example, the matrix multiplication given above is valid only if:
DIM (A())=2 AND DIM (BO)=2 AND DIM (CO)=2 AND DIM(A(),2)=DIM(B(),1) AND DIM(C(),1)=DIM(A(),1) AND DIM(C0,2)=DIM(B(),2)
That is to say, if all three arrays have two dimensions, and that the number of columns of the first equals the number of rows of the second, and that the third array (C()) has the same number of rows as the first and the same number of columns as the second.
There are two special cases of matrix multiplication which are allowed for in BASIC v. As well as the multiplication of two two-dimensional matrices as described, a row matrix may multiply a two-dimensional matrix, and a two-dimensional matrix may multiply a column matrix, the result being a further row matrix or a further column matrix respectively. Now this could be done with two-dimensional matrices in which either i=l or k=l (referring above). However, BASIC V will also allow explicit one-dimensional arrays in this context. Thus:
C()=A() . B()
is valid if the arrays are dimensioned as:
DIM A(j),B(j,k),C(k) row * matrix
DIM A(i,j),B(j),C(i) matrix * column
RISC OS also provides a new matrix function in the form of MOD. When applied to a numeric array this returns the square root of the sum of the squares of the individual elements. Thus, for example:
In addition to the matrix operations described, BASIC v provides a matrix function SUM, which may be used to return the sum of all the elements in an array, or the concatenation of all elements of a string array. Therefore:
Total_Price = SUM(Price())
In the case of a string array, the elements are summed row by row from left to right, as they are for a numeric array.
Under RISC OS, BASIC v provides a second summation function, sumlen, which will return the sum of the lengths of all the strings in a string array.
The whole-array operations provided by BASIC v are very powerful, and should be used, because of their speed of execution, in preference to any other alternatives. They also lend themselves, as you may appreciate from the examples here, to the implementation of spreadsheets and similar tasks. However, there are limitations, and once you start using arrays in this way it is easy to fall into the trap of putting them in situations for which an array is not valid. In general, you cannot create expressions involving arrays. For example:
Total()=(Material 0 +Labour())*1.15
would be rejected. Likewise, when using any of BASIC's functions you cannot substitute an array for a variable. Such statements as:
or anything similar are not allowed. The solution, in the case of the former example, is to express the statement in two separate steps:
In the second case there is no alternative but to use an explicit for...next loop to achieve the desired result:
FOR I%=0 TO DIM(data(),1) Record$(I%)=STR$(data(I%)) NEXT
This means that your coding must explicitly indicate the number of dimensions being used, even if not the size. Because this reduces the flexibility of coding as well as increasing execution time, I do recommend that you try to avoid the need to use functions with array elements unless it is essential, particularly when defining functions and procedures.