An "array" is an ordered collection of values; each value is indexed by an integer value that ranges from 1 to the number of values in the array. You can use the index operator, [index], to retrieve values from the array and store values in the array.
Arrays are very similar to lists, but have some important distinctions:
To use arrays, you must define the Array intrinsic class in your source code. The easiest way to do this is to include the system header file "array.h", which is included with the compiler.
You create an array with the "new" operator. When you create an array, you can pass an integer argument, a list argument, or an array argument.
If you pass an integer argument, the result is an array with the given number of elements, each of which will be initialized to nil.
// create an array with 10 elements
x = new Array(10);
If you pass a list argument, the result is an array with the same number of elements as the list, and with each element of the array set to the corresponding element of the list:
// create an array with 5 elements giving the first
// five letters of the (roman) alphabet
x = new Array(['A', 'B', 'C', 'D', 'E']);
You can also pass an array argument, which results in a new copy of the original array.
// create an array from a list
x = new Array(['one', 'two', 'three', 'four', 'five']);
// create a second copy of x
y = new Array(x);
You can also create an array with a specific number of elements, initializing the elements from a list or array. To do this, pass three arguments: the first is the list or array to copy into the new array; the second is the value 1, indicating that the new array should start with the first element of the source; and the third is the number of elements in the new array. If the new array is larger than the original list or array, the extra elements are set to nil. If the new array is smaller, the extra elements of the original are not used in the new array.
// create an array with 20 elements; the first ten are
// set from the list, the next ten are set to nil
x = new Array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1], 1, 20);
// create an array with 5 elements, which will be set to
// the first five elements from the list
y = new Array([10, 9, 8, 7, 6, 5, 4, 3, 2, 1], 1, 5);
Finally, you can create an array based on a subset of the elements of a list or array. To do this, use the second argument to specify the starting index in the original array of the elements to copy:
x = new Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3, 5);
This creates an array with five elements, copying elements from the list starting with its third element. So, the new array's values are 3, 4, 5, 6, 7. If there aren't enough elements in the original list or array, the remaining elements of the new array are set to nil.
The most important distinction between lists and arrays, and the primary reason to use arrays rather than lists in certain situations, is that arrays use "reference" semantics, while lists use "value" semantics.
The difference is that a list's value can never change, but an array's value can change.
When you do something that modifies a list, such as assigning a value to an element of the list, the operation does not change the list. Instead, it creates a new list that reflects the change, leaving the original list unmodified. TADS automatically updates the variable that contained the list being indexed so that it contains the newly-created list.
In contrast, when you assign a new value to an element of an array, the array's value is changed. No new array object is created.
This might seem like a very obscure difference, but it has two important practical effects. The first is that operations that modify arrays are much cheaper to execute, because they don't result in creating new objects; this means that operations involving a large number of element changes will run faster with arrays than with lists.
The second practical difference is that, whenever you change an array, the change is visible everywhere the array is referenced. In contrast, when you change a list, the change is visible only to the code that made the change.
Consider this example:
local a = [1, 2, 3];
local b = a;
a[2] = 100;
say(b[2]);
What will this example display? At the beginning of the code, we set a to a list, and then we set b to the value in a, so b refers to the same list. So far we have only one object, and both a and b refer to this single object. We next assign a new value, 100, to the second element of a. As we've seen, this cannot change the list that a refers to, because lists can never change; so, what we're doing is creating a new list, copying each element from the original list to the new list, but changing the second element to reflect the assignment. This new list is then assigned to a, so a and b now refer to different lists. So, when we display the second element of b, we see the value "2" displayed, because b still refers to the original, unmodified list.
Now, consider the same example with an array:
local a = new Array([1, 2, 3]);
local b = a;
a[2] = 100;
say(b[2]);
This code looks almost identical, but it displays a different result than the list version. We start out by creating a new array object and assigning it to a, and then we assign the same value to b. Next, we assign 100 to the second element of a. Unlike lists, arrays can be changed, so this assignment simply replaces the value in the array object's second element. No new array object is created, so a and b still refer to the same object. So, when we display b[2] in this example, we see the modified value.
Here's a more interesting example:
f1()
{
local a = new Array(3);
getInfo(a);
"Thanks, <<a[1]>>! This information will allow us to send
you specially targeted advertising based on your credit
history! ";
}
getInfo(x)
{
"Please enter your name: "; x[1] = input();
"Please enter your age: "; x[2] = toInteger(input());
"Please enter your social security number: "; x[3] = input();
}
This is something we couldn't have done with lists: assigning elements of x in getInfo() wouldn't have affected the caller's copy of the list, so the routine wouldn't be able to pass back information this way using lists.
Note that, when you explicitly create a copy of an array, the new copy is not affected by any changes to the original:
x = new Array([1, 2, 3, 4, 5]);
y = new Array(x);
x[3] = 100;
say(y[3]);
This example displays the value "3" (not "100"), because x and y refer to separate objects. Changing a value in the array to which x refers has no effect on the array to which y refers.
The Array class provides several methods for manipulating and converting array values.
applyAll(func) – for each element of the array, this method invokes the callback function func, passing the current element as the single argument, then replaces the array element with the return value from the callback. This method does not create a new array; rather, it modifies the original array. This method returns 'self' as the result value.
This method is useful for transforming the elements of an array by applying a modifier function. For example, if we have an array of numbers, we could use this method to multiply each number in the array by two:
x.applyAll({x: x*2});
This method is also handy for performing complex initializations on a new array. For example, here's a function that creates a new array and initializes it with the first n Fibonacci numbers. Because we're simply initializing the new array, note that the callback function doesn't make any reference to the original element value, but it must still declare a parameter for the argument value so that the arguments passed from applyAll() match the declaration.
createFibonacciArray(n)
{
local f0 = f1 = 1;
return new Array(n).applyAll(new function(x)
{ local ret = f0; f0 = f1; f1 += ret; return ret; });
}
copyFrom(source, sourceStart, destStart, count) – copies values from a list or from another array into this array. This function doesn't create a new array, but simply modifies entries in the 'self' array. source is the source of the values; it must be either an array or a list. sourceStart is an index into source, and specifies the first element of source that is to be copied. destStart is an index into the 'self' array, and specifies the first element of the array that is to be modified. count is the number of elements to modify. The method copies elements from source into the 'self' array, one at a time, until it reaches the last element of either source or 'self', or has copied the number of elements specified by count.
Calling this method is equivalent to writing a code fragment like this:
for (local i = 0 ; i < count ; ++i)
dest[destStart + i] = source[sourceStart + i];
(This code is not exactly equivalent, because it does not check that the indices are in range. To make the code exactly the same as the copyFrom() method, the loop condition would have to check that the index values are not beyond either the source's or destination's maximum index value.) Calling copyFrom() is easier and less error-prone than explicitly writing the loop, though, and it is considerably faster because it is implemented as native code.
The copyFrom() method simply returns 'self'; this is convenient for expressions like this:
x = new Array(20).copyFrom(lst, 3, 2, 5);
fillValue(value, start?, count?) – fills elements of this array with value. If only value is specified, this method simply stores value in every element of the 'self' array. If start is specified, it gives the starting index; the method fills values starting with start, to the end of the array. If both start and count are specified, count gives the maximum number of elements to fill.
This method is equivalent to writing a code fragment like this:
for (local i = 0 ; i < count ; ++i)
dest[start + i] = value;
(To make the code exactly equivalent, you would have to modify the loop condition so that it checks to ensure that the index is in range.) Calling fillValue() is easier than writing this code fragment, though, and considerably faster because it is implemented as native code.
This method returns 'self', which allows for expressions like this:
x = new Array(20).fillValue('A');
length() – returns an integer giving the number of elements in the array. This is the same as the number of elements specified when the array was originally created, because an array never changes in size.
toList(start?, count?) – creates and returns a new list value based on the array. With no arguments, the new list has the same number of elements as the original array, and each element of the list is a copy of the corresponding element of the array. If start is specified, it gives the starting index in the array for the list; elements of the array before start are not included in the list. If count is specified, it indicates the number of elements of the array, starting at start, to copy into the list.
This method is useful when you need to pass a value to a routine that requires a list value. Arrays cannot generally be passed to routines requiring list values, so you can use this routine to create a list with the same values as the array.
This method does not modify the array.
subset(func) – creates and returns a new array containing the elements of this array for which the callback function func returns true (i.e., any value other than nil or the integer value 0). For each element of the source array, this method invokes the callback function, passing the value of the current element as the callback function's single argument. If the callback returns nil or the integer value 0, the method omits the element from the result; otherwise, the method includes the element in the result array. The new array's elements will be in the same order as the selected elements from the source array.
This method does not modify the original array.
This example uses a short-form anonymous function to create to create a new array that contains only the elements from an original array whose values are greater than 10.
y = x.subset({x: x > 10});