Arrays and Function Parameter Lists

When an array parameter (formal) of a function or subroutine is declared, the number of dimensions is specified, but the extent of (number of elements in) each dimension is not specified. This allows the programmer some freedom when invoking such a function. For example, a function may be defined to take a one-dimensional array and compute the sum of the elements in the array. A single function can be written to take a one-dimensional array of any size and correctly compute the sum. (Because AKD BASIC checks array bounds at run time on each access, there is no risk that a function will read or write outside the bounds of the array.)

When a formal parameter to a function is an array, instead of specifying the extent of each dimension, a list of variables is used to both implicitly specify the number of dimensions and to hold the extent of each dimension. These variables are read-only and cannot be modified within the function.

Adopt a convention for assigning names to placeholders.  One such convention is to use the name of the array with a numerical suffix.  For example,

function f(a(a1,a2,a3) as integer) as integer 

where a1, a2, and a3 are the variables that get the extents of the array, a.

The function f above would be called as follows:

dim x_array(3,4,5) as integer      
dim y_array(1,2,10) as integer
print f(x_array()) + f(y_array())

In both invocations of f, the function correctly determines the extent of each dimension of the passed array.

Remember that when passing an array to a function, the type of the array must match EXACTLY with the type expected by the function.  Unlike scalar arguments (implicitly coerced from float to int or int to float), arrays are NOT coerced.  An attempt to pass an integer array to a function that expects a float array results in a compile-time error.

Optimizations

As mentioned in an earlier section, constant definitions are completely ‘folded’ at the point of definition. This is efficient code. Constant expressions inside AKD BASIC statements are also folded under certain conditions. For example, in the statement:

const PI = 3.1415926535 
Main Print PI^2 End Main

The value of PI^2 is not computed at run-time. It is detected as a constant value and pre-computed by the compiler as a single literal constant to be printed.

Similarly, the literal constant 3*4*PI in

x = 3 * 4 * PI * x

is folded at compile-time, leaving only one multiplication to be performed at run-time.

However, certain constant expressions are not folded.  For example:

x = 3 * PI * x * 4

is computed at run-time, involving 3 multiplications because the analysis of constant expressions does not attempt to exploit algebraic commutativity laws.  Since the basic arithmetic operators are ‘left associative’, you can ensure the best performance by grouping constant factors together towards the left (or using a new constant definition).

If a function is not referenced (transitively from MAIN, plus any interrupt handlers), the compiler does not generate code for it. So, you can freely $include libraries with unused code (e.g., a comprehensive library containing functions supporting several possible axis configurations). Although the compiler parses and type-checks all the included source, it does not generate code into the downloaded program.

If select-case cases are all constants, more efficient code is generated. If a case is a variable, the generated code is equivalent to a string of if-then-else statements for all cases.

If any of the cases is an open-ended range (e.g., is 10), or covers a large range (e.g., 1 to 1000), a fast table-lookup is generated.

If all of the cases are constant, and can be grouped into locally dense subsets, the fastest possible code is generated — a binary search of dispatch tables, followed by an indirect jump through the table. If speed is a consideration, keep your cases constant and close together. (values form a reasonably dense set.)

The compiler performs limited dead-code elimination based on simple constant analysis.  For example:

const DEBUGGING = FALSE 
Main
dim i, sum as integer
for i = 1 to 10 sum = sum + i if DEBUGGING then
print
“partial sum is ”;sum next i End Main

Since the value of DEBUGGING is FALSE, the compiler recognizes that the printing of the partial sum never happens and does not generate the print statement. This allows you to place debugging code in strategic locations in your programs and effectively disable it when shipping a production version (shrinks the size of the generated code).

This dead-code elimination also applies to functions whose only point of reference lies in eliminated code. The functions themselves become dead-code and no code is generated for their definitions.

The compiler does not eliminate the print statement from the following program:

dim DEBUGGING as integer
Main
dim i, sum as integer DEBUGGING = FALSE for i = 1 to 10 sum = sum + i if DEBUGGING
print“partial sum is ”;sum next i End Main

In this case, the print statement never executes, but the code to implement is generated because the value of the integer DEBUGGING could be changed by the AKD’s Integrated Development Environment Debugger at runtime, causing the print statement to be executed!