Skip to main content
Skip table of contents

Defining your own

It is possible to use locally user-defined functions for repetitive tasks. Such functions can be defined locally in the script or in customization, to allow shared usage of functions by all the system users.

Functions can be also redefined and undefined. Because the language construction is function based, core operations like IF, WHILE, even DEFUN are also functions. If the factory-provided language function are undefined or redefined, the language might become locally (within single script execution) unusable.

Functions access global variables by default. Watch out when using common variable names such as i for iterating to avoid clashing with other functions doing the same. Using LOCALS or WITH is highly recommended to avoid common pitfalls.

DEFUN - defining functions

DEFUN has a simple syntax

DEFUN( <new_function_name>, <argument_name_list>, <function_source_code> )

Function source code is a regular ASTER script, with operations separated by semicolons ;.

The return value of the function is the last value evaluated in the function body, similar to how it works for the complete scripts.

Examples

CODE
DEFUN("ADD_ONE",("A"),A+1);         # Define function ADD_ONE
ADD_ONE(1)                          # Returns 2
CODE
DEFUN("MY_POWER",("X","Y"),POWER(X,Y));    # Simple example for a new POWER function
POWER(2,2) == MY_POWER(2,2)

LAMBDA - inline functions

Sometimes it’s not needed to define a named function, which is when inline functions come useful. ASTER allows defining inline functions and passing them around using variables. In technical terms, functions are first-class citizens in the language.

LAMBDA( <argument_name_list>, <function_source_code> )

Function source code is a regular ASTER script, with operations separated by semicolons ;.

The return value of the function is the last value evaluated in the function body, similar to how it works for the complete scripts. Lambda call returns a function object reference that can be used as argument or passed as a function where it’s needed (e.g. in sorting).

Examples

CODE
# sorting
i=0;
while( i<100,
   i=i+1;list[i]=ROUND(RANDOM(1000,9000),3));       # creating a list of 100 random numbers

sorted = qsort(list, lambda(("a","b"), A<B))        # sorted contains a sorted list now
CODE
# map operation
fsquare = lambda("x",x*x);

defun("map",("list","fun"),
    with(i=0;r=0;c=count(list),
        unset(r);
        while(i<c,
            i=i+1;
            r[i] = {fun}(list[i]));               # note the {} notation to call 
        r));                                      # variable named function

a=(1,2,3,4);
b=map(a,fsquare);

b[1] & " " & b[2] & " " & b[3] & " " & b[4]       # Result: 1.0 4.0 9.0 16.0

Variable scoping

All the variables defined in the argument list name are automatically shadowing variables of the same name in the script. In other words, function arguments are locally scoped. Global variables of the same name are not accessible within the function body.

Example

CODE
A=5;
DEFUN("AP",("A"),A+1);         # Define function AP, the global variable A is not accesible
AP(1);                         # Returns 2, as the global value of A was shadowed
A==5                           # TRUE - Global variable is not changed

More complex variable scoping scenarios

In case the newly defined function is complex and needs more variables for implementation, it makes sense to define these variables also as locals, specifically for complex calculation sequences.

Function LOCALS handles this scenario. It may be used also outside function definition, if needed for clarity or convenience. Nesting is also possible.

LOCALS( <variable_name_list>, <expressions> )

Examples

CODE
A=5;C=10;
D = LOCALS(("A","B"),                           # existing A and B will be shadowed
  A=10;
  A=A*A;
  B=A/2;
  C=B);                                         # Accesses global C !
"A=" & A & " B=" & B & " C=" & C & " D=" & D    # A=5 B= C=50 D=50

Convenient solution for scoping and assignments

LOCALS being a basic ASTER construct might be useful for complex variable scoping scenarios. For more usual cases, where assignments are usually part of the declaration, using WITH might be much more convenient. It analyzes the assignment code automatically shadowing named variables, so that the code can be leaner and less prone to errors as shadowed variables must be mentioned only once.

WITH( <assignment_code>, <expressions> )

Example

CODE
a=10;                       # global A
b=20;                       # global B
c=with(a=5;b=10,a+b);       # shadows A and B, before executing assignment
a & " " & b &  " " & c      # returns 10 20 15 

# 15 being sum of 5+10, original values of A and B protected automatically by shadowing

Calling other functions, including recurrence

It is possible to call other functions, or even the newly defined function itself, within the function body. It is also allowed to call DEFUN from within another function, making it possible to create macros - a code that is writing new code.

Recurrent function calling is a common use case. Below example showcases using recurrence (specifically in this case called recursion) to calculate factorial of a number.

The formula for factorial is:

image-20250319-212936.png

And its recursive definition:

image-20250319-213005.png

Example

CODE
DEFUN("S","X",IF(X==1,1,S(X-1)*X));   # S is our new factorial function
S(5) == (5*4*3*2*1)                   # TRUE - 5 * 4 * 3 * 2 * 1 = 120
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.