User-definable functions, procedures. Local/global scope

User-definable functions allow to encapsulate user code into easy-to-use modules that can be user in many places without need to copy the same code over and over again.

Functions must have a definition. The function definition includes the function body — the code that executes when the function is called.

A function definition establishes the name, and attributes (or parameters) of a function. A function definition must precede the call to the function. The definition starts with function keyword then follows function name, opening parenthesis then optional list of arguments and closing parenthesis. Later comes function body enclosed in curly braces.

A function call passes execution control from the calling function to the called function. The arguments, if any, are passed by value to the called function. Execution of a return statement in the called function returns control and possibly a value to the calling function.

If the function does not consist of any return statement (does not return anything) then we call it a procedure.

Following is an example of function definition:

// the following function is 2nd order smoother

function IIR2( input, f0, f1, f2 )
{
    result[
0 ] = input[ 0 ];
    result[
1 ] = input[ 1 ];

   
for( i = 2; i < BarCount; i++ )
    {
       result[ i ] = f0 * input[ i ] +
                     f1 * result[ i -
1 ] +
                     f2 * result[ i -
2 ];
    }

   
return result;
}

Plot( Close, "Price", colorBlack, styleCandle );
Plot( IIR2( Close, 0.2, 1.4, -0.6 ), "function example", colorRed );


In this code IIR2 is a user-defined function. input, f0, f1, f2 are formal parameters of the functions.
At the time of function call the values of arguments are passed in these variables. Formal parameters behave like local variables.
Later we have result and i which are local variables. Local variables are visible inside function only. If any other function uses the same variable name they won't interfere between each other.

Due to the fact that AFL does not require to declare variables the decision whenever given variable is treated as local or global is taken depends on where it is FIRST USED.

If given identifier appears first INSIDE function definition - then it is treated as LOCAL variable.
If given identifier appears first OUTSIDE function definition - then it is treated as GLOBAL variable.

This default behaviour can be however overriden using global and local keywords (introduced in 4.36) - see example 2.

Example (commentary):

k = 4; // this is GLOBAL variable

function f( x )
{
   z =
3; // this is LOCAL variable
   return z * x * k; // 'k' here references global variable k (first used above outside function)
}

z =
5; // this is GLOBAL variable with the same name as local variable in function f

"The value of z before function call :" + WriteVal( z );

// Now even if we call function
// the value of our global variable z
// is not affected by function call because
// global variable z and local variable z are separate and
// arguments are passed by value (not by reference)

"The result of f( z ) = " + WriteVal( f( z ) );

"The value of z after function call is unchanged : " + WriteVal( z );

Example 2: Using local and global keywords to override default visibility rules:

VariableA = 5; // implict global variable

function Test()
{
   local VariableA;  // explicit local variable with the same identifier as global
   global VariableB; // explicit global variable not defined earlier
                     // may be used to return more than one value from the function

   VariableA =
99;
   VariableB =
333;
}

VariableB =
1; // global variable

"Before function call";
"VariableA = " + VariableA;
"VariableB = " + VariableB;

Test();

"After function call";
"VariableA = " + VariableA + " (not affected by function call )";
"VariableB = " + VariableB + " (affected by the function call )"

At the end of the function we can see 'return' statement that is used to return the result to the caller. Note that currently return statement must be placed at the very end of the function.

It is also possible to write a procedure (a function that returns nothing (void))

procedure SinePlotter( Freq, ColorIndex )
{
   pname =
"Line"+WriteVal(ColorIndex,1.0);
   array =
sin( Cum( Freq * 0.01 ) );
   
Plot( array, pname , colorRed + ColorIndex, styleThick );
}

for( n = 1; n < 10; n++ )
{
    SinePlotter( n/
2+Cum(0.01), n );
}

Note that although there are two separate keywords 'function' and 'procedure' AmiBroker currently treats them the same (they both accept return values but not require them), but in the future the rules maight get enforced to use
return statement ONLY in conjunction with function keyword. So it is advised to use function keyword in case when your function returns any value and procedure keyword otherwise.

Note also that recursion (having a function call itself from within itself) is NOT supported as for now.

More information

Please read also Understanding how AFL works article to learn more.