The Array Intrinsic Class

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.

Creating an Array

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.

Reference Semantics

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;
  tadsSay(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;
  tadsSay(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;
  tadsSay(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.

Array Iterations

An Array is a subclass of Collection, so you can use the createIterator() method to create an Iterator to iterate over the elements of the array.  The Iterator that an Array creates is called an IndexedIterator; it visits the elements of the array in index order (the first element visited is the element at index 1, the second is the element at index 2, and so forth).

Array Methods

Array is a subclass of Collection, and thus includes all Collection methods.  In addition, Array defines the methods listed below.

 

appendUnique(val) – appends the elements of the list or array val to this array, returning a new array consisting only of the unique elements of the combination.  The new array will be created with exactly as many elements as are necessary to hold the result, so the new array's size might be different from the original array's size and from the size of val.  Each value appearing in the result array will appear only once.

 

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);

countOf(val) – returns the number of elements in the array whose values equal val.

 

countWhich(cond) – returns the number of elements in the array for which the callback function cond returns a non-false value (anything but nil or 0).  For each element in the array, the method invokes cond, passing the element as the argument to the callback.  If cond returns anything but nil or 0, the method counts the element.  After invoking cond for each element, the method returns the number of elements for which cond returned a non-false value.

 

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');

 

getUnique() – returns a new array consisting of the unique elements of the original array.  For each value in the original array, the value will appear in the new array only once.  The order of the elements in the new array is that of the first appearances of the unique elements of the original array.  For example, if the original array's elements are, in order, 1, 5, 2, 5, 3, 5, 4, 5, this method will return a new array whose elements are, in order, 1, 5, 2, 3, 4.  Note that the size of the new array is just large enough to hold only the unique elements, so the new array might be smaller than the original array.

 

indexOf(val) – finds the first element of the array whose value equals val, and returns the index of the element.  Returns nil if none of the array's elements equals val.

 

indexWhich(cond) – finds the first element for which the given condition is true.  The method iterates through the elements of the array, starting at the first element and proceeding in order, and applies the callback function cond to each element.  The callback takes one argument, which is the value of the array element, and returns a condition result value.  For each element, if the callback function returns a non-false value (i.e., any value except nil or zero), the method immediately stops the iteration and returns the index of that element.  If the callback returns a false value (nil or zero) for every element of the array, the method returns nil.

 

forEach(func) – invokes the callback function func for each element, in order from first to last.  The callback function takes one argument, which is the current element, and returns no value.  This method returns no value.  This method is a convenient means of executing some code for each element of the array.

 

lastIndexOf(val) – returns the index of the last element in the array whose value equals val.  If none of the elements in the array matches the given value, the method returns nil.

 

lastIndexWhich(cond) – finds the last element for which the given condition is true.  This method is similar to indexWhich(cond), but scans the array in reverse order, starting with the last element and working backwards.  Returns the index of the matching element, or nil if the condition returns false for every element.

 

lastValWhich(cond) – finds the last element for which the given condition is true, and returns the element's value.  This method is similar to lastIndexWhich(cond), but returns the value of the matching element rather than its index.

 

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.

 

mapAll(func) – creates a new array consisting of the results of applying the callback function func to each element of the original array.  This method is similar to applyAll(func), but rather than modifying the elements of the original array, this method creates a new array, and leaves the elements of the original array unchanged.  The return value is the new array.

 

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.

 

sort(descending?, comparisonFunction?) – re-orders the elements of the array into sorted order.  By default, this method sorts the elements of the array into ascending order, but you can reverse this ordering by specifying true for the descending argument.

 

The optional comparisonFunction can be used to specify the ordering of the result.  If this argument is not specified (or is nil), the method will sort the elements according to the standard system ordering of values; hence, the elements must be of comparable types (such as all integers or all strings).  By specifying a comparison function, you can provide your own special ordering, and you can also sort values that have no system-defined order, such as object values.

 

The comparisonFunction works the same way as the for the List class's sort() method.

 

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});

 

valWhich(cond) – returns the value of the first element for which the callback function cond returns a non-false value (i.e., any value other than nil or 0).  The method applies the callback to each element of the function, starting with the first, and calls the function for each element in turn until cond returns a non-false value.  Returns nil if the callback returns a false value for every element.  This function is almost the same as indexWhich(cond), but returns the value of the first element for which cond returns non-false rather than the index of the element.