Function
A function is a reusable program unit that performs a specific operation and may return a single result. Functions are designed to encapsulate frequently used logic.
- Functions are stateless meaning none of the variable values are retained between calls.
- Functions are deterministic in that for a given set of inputs, a function will always produce the same result.
- Functions are defined in a global scope and can be called in any location that accepts an expression.
- Functions are not types and so cannot be assigned to a variable or passed as a parameter.
- Inputs to a Function are passed by value.
- InOuts to a Function are passed by reference.
- Outputs to a Function are passed by value.
Syntax
The FUNCTION keyword begins the function declaration while the END_FUNCTION keyword closes the function declaration. Adding a type after the name of the function
indicates that the function has a return value. Any number of variable blocks can be added, but keep in mind that local variables are initialized on every call.
FUNCTION F_AddTwoNumbers : INT
VAR_INPUT
IN1 : INT;
IN2 : INT;
END_VAR
// Local variables are not retained
VAR
defaultZero : INT;
END_VAR
// Assign the return value using the same name
// as the function
F_AddTwoNumbers := IN1 + IN2 + defaultZero;
// defaultZero is assigned a value, but
// on every single call, it will be re-initialized to 0
defaultZero := IN1;
END_FUNCTION
Call
VAR
val : INT;
END_VAR
VAR CONSTANT
FIVE : INT := 5;
END_VAR
// Assign parameters by index
val := F_AddTwoNumbers(3, FIVE);
print(val); // 8
// Assign parameters by name, order does not matter
val := F_AddTwoNumbers(IN2 := FIVE, IN1 := 2);
print(val); // 7
// It is acceptable to not assign the function return
// although this may not be intended
F_AddTwoNumbers(1, 2);
Recursion
A recursive function is a function that calls itself within the function body. A simple example would be a function. to compute the factorial value of a number. For example 5! is 5 * 4 * 3 * 2 * 1 = 120. A recursive function that computes this value looks like the following.
FUNCTION F_Factorial : INT
VAR_INPUT
n : INT;
END_VAR
IF n <= 1 THEN
F_Factorial := 1;
ELSE
F_Factorial := n * F_Factorial(n - 1);
END_IF;
END_FUNCTION
VAR
val : INT;
END_VAR
val := F_Factorial(5); // 120
What happens if we try to compute 100! ? In this scenario, an error will be returned that a stack frame limit has been exceeded. Consider that each call of the recursive function creates and pushes a 'frame' onto the stack. On most actual controllers, the stack is allowed to grow until a stack overflow occurs. As a guardrail, the execution engine allows a maximum of 20 stack frames to prevent infinite recursion.
val := F_Factorial(20); // Try it! This will result in an error
Because functions are exist within the global scope, two functions are allowed to call one another (mutual recursion). An admittedly bad example would be two functions for determining if the input value is an even or odd number. The functions call one another subtracting a value from the input until reaching zero.
FUNCTION F_IsEven : BOOL
VAR_INPUT
n : INT;
END_VAR
IF n = 0 THEN
F_IsEven := TRUE; // Base case: 0 is even
ELSE
F_IsEven := F_IsOdd(n - 1); // Recursive call to IsOdd
END_IF;
END_FUNCTION
FUNCTION F_IsOdd : BOOL
VAR_INPUT
n : INT;
END_VAR
IF n = 0 THEN
F_IsOdd := FALSE; // Base case: 0 is not odd
ELSE
F_IsOdd := F_IsEven(n - 1); // Recursive call to IsEven
END_IF;
END_FUNCTION
VAR
val : BOOL;
END_VAR
val := F_IsEven(10);