FutureBasic Logo

<<    Index    >> FutureBasic 5

local fn   statement



Syntax:
[clear] local fn functionName [(arg1 [,arg2 ...])]
   [statementBlock]
end fn [= expr]


Description:
This statement marks the beginning of an FutureBasic local function. The end of the local function is marked by the end fn statement. A local function is a named collection of statements which can be accessed and executed as a unit by referring to the function's name (see the fn <userFunction> statement). All variables and arrays referenced in a local function (except those explicitly declared as "global") are local to the function, which means they do not have any influence outside of the function; any identically-named variables which appear outside of the function are actually different variables, and occupy a different place in memory, than the function's local variables. (An exception to this rule occurs when an array is listed as one of the function's formal parameters; see more about this below.) When your program "calls" (executes) a local function, you can pass data into the function by means of its parameter list (also called its argument list), and you can receive a value back from the function by means of its return value. Local functions allow you to encapsulate complex programming tasks; they're a fundamental and extremely powerful programming construct.
In addition to marking the beginning of the function, the local fn statement also declares the function's name, the data type of its return value (if any), and the number and types of its input parameters (if any). A local function can be placed anywhere in the program, except inside another local function; you should also not place a local function inside a "block" structure such as long if...end if, etc. The statements in statementBlock can contain anything except the following:
Adding the clear keyword causes all of the function's local variables and arrays (except the parameter variables arg1, arg2 etc.) to be initialized to zeros, null strings or empty records, each time the function is called. Otherwise, the local variables and arrays will have unpredictable initial values. (You can accomplish the same effect by using the local statement with the clear keyword; see the local statement for more information.)
The functionName must be unique; that is, it must be different from the name used in any other local fn, def fn using statement anywhere else in the program. If the function is to return a value, then you should specify the data type of the return value by including an appropriate type-identifier suffix at the end of functionName. For example, a local function which is to return a string value might be declared as follows:
  local fn FullName$(idNum&)
The default data type of a function's return value is "long integer"; if the function is to return a long integer value, you can either include the "&" type-identifier suffix or omit it. No type-identifier suffix should be appended to functionName if the function does not return a value.
In order to execute the statements in statementBlock, your program must "call" the function using the fn <userFunction> statement. The fn <userFunction> statement can appear anywhere in your program, as long as the function it calls is either defined (using the local fn statement) or prototyped (using the def fn <prototype> statement) somewhere above the fn <userFunction> statement. This restriction is required in order to allow your program to compile; however, the actual order of execution of your program's statements is not affected by where you place your local fn's.

Function Parameters
Each of the parameters arg1, arg2 etc. can have any of the following forms:

Parameter formDescription
SimpleVarA simple numeric or string variable. Cannot be a record variable, a record field nor an array element.
ptrVar As Pointer [To unType](See below.)
@longVar&longVar& is a simple long-integer variable.
@ptrVar As Pointer [To unType](See below.)
tableau(dim1[,dim2...])array is a numeric or string array (not an array of records), and dim1,

The parameters in the local fn statement are called the function's "formal arguments." They must not be global variables. You should not declare the formal argument variables in a dim statement; they are implicitly declared by the local fn statement.
When your program calls the function, the arguments passed to it in the fn <userFunction> statement are called the "actual arguments." They must match the function's formal arguments in number, and they must be of "compatible types" (see fn <userFunction> for more information). Each time the function is called, values are assigned to its formal arguments as follows:
If the local function has no parameters, you should omit the parentheses after functionName.

Passing an Array of Unknown Size
Sometimes it's useful to write a local function which operates on an array passed in its parameter list, without knowing in advance the size of the passed array. For example, suppose you wish to write a function which sorts the elements of a long integer array, and you want it to work regardless of the declared size of the passed array.
When you declare an array as a formal parameter, FutureBasic ignores the value of the array's first declared dimension in the local fn statement. For example, suppose we have a function defined like this:
local fn SetElements(anArray&(1,7), max&)
  'Set each element to 1492 in the array:
  for i& = 0 to max&
    for j = 0 to 7
      anArray&(i&,j) = 1492
    next
  next
end fn

When we pass a long integer array to fn SetElements, the passed array can have any size as its first declared dimension, as long as it has a second dimension declared as 7. For example:
dim arrayOne&(1250,7), arrayTwo&(465,7)
fn SetElements(arrayOne&(0,0), 1250)
fn SetElements(arrayTwo&(0,0), 465)

Within the function, we can safely manipulate elements in the array as long as the subscripts we use don't exceed the declared dimensions of the actual array that was passed. Thus, in fn SetElements, we can set the first subscript in anArray& to values much greater than 1, even though anArray& was "declared" with dimensions (1,7).
Note: you do not have equal freedom with the second, third, etc. dimensions of the parameter array. If the array is multi-dimensional, the second and subsequent dimensions must be declared with the same values in both the "formal" array parameter (in the local fn statement) and the external dim statement that declares the actual passed array.

Returning a Value
If you specify an expr in the end fn statement, the function will "return" the value of expr. This can be any expression which is compatible with the type-identifier suffix (if any) that appears in functionName. When your function "returns" a value, it means that you can reference the function (using fn <userFunction>) as part of a string or numeric expression, and the function's return value will be substituted in the expression. For example:
   maxPuppets = 6 * fn storeCount%(x)
Here, if fn storeCount%(x) returns a value of 7, then the value 42 will be assigned to maxPuppets.

The Lifespan of Local Variables
The memory space for a function's local variables is reserved when the function is called. This memory is released after the end fn statement is executed. Therefore, you should never make reference to a local variable's address after the function has finished executing; in particular, you should never pass a local variable's address back to the routine that called the function. For example:
'DON'T do THIS!
local fn myFunction&(x,y,z)
  dim r#
  r# = sqr(x*x + y*y + z*z)
end fn = @r#

   rAddr& = fn myFunction&(x,y,z)
After the preceding is executed, rAddr& points to an area of memory (the old address of r#) which is no longer reserved, and which should not be used.
On the other hand, it is permissible to pass a local variable's address into another local function. This works because the first local function has not yet finished executing when it calls the second local function. Therefore, the memory space holding the first function's local variables is still reserved intact while the second function executes.
'THIS IS OKAY:
local fn FirstFn
  dim 255 myString$
  'Pass address of local var into another fn:
  fn SecondFN(@myString$)
end fn
  :
local fn SecondFn(strAddr&)
  BlockMove @gString$, strAddr&, len(gString$)+1
end fn


Recursive functions
You can have several functions executing simultaneously, in the sense that one function can call a second function, which can call a third, and so on. If you design your function calls in such a way that a function can call a function that is already executing, then you have a "recursive function." The most obvious (but not the only) example of a recursive function is any function which calls itself. When that happens, we say that two (or more) "instances" of the function are executing simultaneously.
In FutureBasic, every currently executing "instance" of a local function maintains its own private set of local variables, and they don't interfere with the local variables of any other executing instance of that function. Calling a function recursively is very much like calling a "different" function which just happens to contain exactly the same program lines.
Although recursive functions may at first seem like a bizarre concept, they are perfectly acceptable, and often very useful. For example, here is a short program which prints all the permutations of the characters contained in a given input string; note that FNpermute_r calls itself. It would be very difficult to write such a program without using recursive functions.
'Function prototypes:
def fn Permute(aString$)
def fn permute_r(prefix$, suffix$)

input "Enter a word: "; theWord$
fn Permute(theWord$)
end

local fn Permute(aString$)
   'Prints all permutations of the letters in aString$
   fn permute_r("", aString$)
end fn

local fn permute_r(prefix$, suffix$)
  'Prints all permutations of prefix$+suffix$
  'that start with prefix$
  long if suffix$ = ""
    print prefix$
  xelse
    for i = 1 to len(suffix$)
      'Move the i-th letter of suffix$ over to newprefix$:
      newprefix$ = prefix$ + mid$(suffix$, i, 1)
      newsuffix$ = left$(suffix$,i-1) + mid$(suffix$,i+1)
      'Now print all permutations that
      'start with newprefix$
      fn permute_r(newprefix$, newsuffix$)
    next
  end if
end fn


Returning Multiple Values
The end fn statement can return only a single numeric or string expression. But many times, it's useful to have a local function which can return more than one value. The way to accomplish this is through the function's parameter list. If you give the function access to the address of some external variable or array, then the function can alter the contents at that address, effectively modifying the value of that variable or array.
There are three ways to pass an address to your function:
See Also:
fn <userFunction>; local; @fn; def fn <prototype>