Functions/Subs
<< Click to Display Table of Contents >> Navigation: ThinBASIC Core Language > Script structure > Functions/Subs |
Functions and Subs are the same in thinBasic, so here use the two terms as synonyms.
Subs are just functions not returning any value.
Function Procedures
A Function procedure is a series of thinBasic statements enclosed by the FUNCTION and END FUNCTION statements.
A Function procedure is similar to a Sub procedure in other languages, but can also return a value.
A Function procedure can take arguments (constants, variables, or expressions that are passed to it by a calling procedure). If a Function procedure has no arguments, its Function statement must include an empty set of parentheses.
A Function returns a value by assigning a value to FUNCTION keyword in one or more statements of the procedure. The return type of a Function is always automatically converted to the function return type.
Function declaration:
Function FunctionName [([arguments])] [As Type [PTR]]
[Local variable list]
[Dim variable list]
{statements}
[Function = ReturnValue]
{statements}
[Exit Function]
{statements}
End Function
In the following example, the Celsius function calculates degrees Celsius from degrees Fahrenheit. When the function is called from the ConvertTemp Sub procedure, a variable containing the argument value is passed to the function. The result of the calculation is returned to the calling procedure and displayed in a message box.
Function Celsius(fDegrees As Number) As Number
Function = (fDegrees - 32) * 5 / 9
End Function
If a function is specified without any AS clause (so no indication about what type of data the function is returning), AS LONG is assumed by default.
Getting data into a function: parameters
Data is passed to functions using parameters.
Parameters serve as placeholders for the data you want to pass into your procedure. You can name your parameters anything that is valid as a variable name.
When you create a procedure, parentheses must be included after the name of the procedure. Any parameter is placed inside these parentheses, separated by commas.
In the following example, InVal is a placeholder for the value being passed into the Factorial function for factorial calculation:
Function Factorial(InVal As Number) As Number
Dim Count As Long
Dim RetVal As Long
RetVal = 1
For Count = 2 To InVal
RetVal = RetVal * Count
Next
Function = RetVal
End Function
Normally, thinBasic passes parameters to a Function by copy (BYCOPY). It means that a copy of the passed data will be created locally inside the function and any change to the local data will not effect the original data.
Parameters can also be passed by reference (BYREF). In this way, the address of the variable is passed and the function has to look at that address to get the value of the parameter. Passing parameters by reference will not make a copy of the original data but a reference to the original data will be created and any change to local function parameter will effect also original variable.
The passing parameter method is specified by appending a clause in front of the parameter name. For example:
FUNCTION Test(A AS INTEGER) '---Integer passed by copy, default one
FUNCTION Test(BYVAL A AS INTEGER) '---Integer passed by copy, explicitly
FUNCTION Test(BYCOPY A AS INTEGER) '---Integer passed by copy, explicitly
FUNCTION Test(BYREF A AS INTEGER) '---Integer passed by reference
Parameters can be declared OPTIONAL. This will instruct thinBasic that, when the function will be called one or more parameters may be missed.
Parameters declared as OPTIONAL will be in any case created inside the function but if missed during function calling, default data will be assigned.
OPTIONAL keyword can be used for every parameter. Once used, all parameter following the first declared as OPTIONAL will be OPTIONAL by default.
'---First parameter if not optional
'---Second parameter is optional
FUNCTION Test(MyFirstVar AS INTEGER, OPTIONAL MySecondVar AS LONG)
'---First parameter if not optional
'---Second parameter is optional
'---Third parameter will be optional by default
FUNCTION Test(MyFirstVar AS INTEGER, OPTIONAL MySecondVar AS LONG, MyThirdVar AS EXT)
Parameters type can be omitted. In this case BYVAL ... AS LONG will be assumed.
For example:
Function AddMessage( _
hDlg As Long , _
Message As String, _
lObject As String, _
Optional Details As String, _
P1, P2, P3)
Variable type for parameters P1, P2, P3 is not defined. thinBasic will assume:
•BYVAL P1 as LONG
•BYVAL P1 as LONG
•BYVAL P1 as LONG
ByCopy is an alias of ByVal
Passing UDT data structure to a function.
UDT (User Defined Types) can be passed to a function both ByRef or ByVal.
UDT arrays can only be passed ByRef.
When it is not know what UDT type can be passed at script time but only at RunTime a special pseudo data type called AnyType can be used. Function will determine the passed UDT type at runtime.
Example, a recursive QuickSort function that can sort any UDT array type:
'------------------------------------------------------------------------------
' Generic function used to sort any UDT by any UDT field name
' Function takes advance of
' - new pseudo type AnyUdt that means that UDT is determined at runtime
' - new feature that allows to determine UDT element name at runtime
' including its name between () after the point
'------------------------------------------------------------------------------
function AnyUdt_QSort(byref t1() as AnyType, firstIndex as long, lastIndex as long, sFieldName as String)
local low as long
local high as long
local pivotValue like typeof(t1(1).(sFieldName)) '<---Same type as the UDT element passed by name
local tTemp like typeof(t1) '<---Same type as UDT passed at Runtime
low = firstIndex
high = lastIndex
pivotValue = T1((firstIndex + lastIndex) / 2).(sFieldName) '---UDT element name is determined at runtime
Repeat
While T1(low).(sFieldName) < pivotValue '---UDT element name is determined at runtime
low += 1
Wend
While T1(high).(sFieldName) > pivotValue '---UDT element name is determined at runtime
high -= 1
Wend
If low <= high then
'---SWAP
tTemp = T1(low)
T1(low) = T1(high)
T1(high) = tTemp
'---
low += 1
high -= 1
End If
Until low > high
If firstIndex < high then
AnyUdt_QSort(T1, firstIndex, high, sFieldName)
End If
If low < lastIndex then
AnyUdt_QSort(T1, low, lastIndex, sFieldName)
End If
End function
Variadic parameters
Sometimes it is not possible to know in advance how many parameters can be passed into a sub/function.
ThinBASIC solve this problem adding the so called "variadic" parameters: just indicate a parameter as an array with the ANY keyword inside round brackets.
So: parameter name followed by "(", followed by "Any", followed by ")".
Here an example:
Function Variadic1(FirstVariadicParam(Any) As Ext, sBuffers(Any) As String) As Long
PrintL "From inside " & Function_Name
PrintL "Variadic parameter 'FirstVariadicParam' number of elements:", UBound(FirstVariadicParam)
PrintL "Variadic parameter 'sBuffers' number of elements:", UBound(sBuffers)
PrintL "----------------------------------------------------------------------------"
PrintL "Listing all values of 'FirstVariadicParam':"
For lCount As Long = 1 To UBound(FirstVariadicParam)
PrintL " - Element", lCount, "has value of", FirstVariadicParam(lCount)
Next
PrintL
Function = UBound(FirstVariadicParam)
End Function
and later on call the function in this way, that is surround the values of a variadic parameter in round brackets:
'---Because first and second parameters are variadic, pass any number of numeric/string expressions inside round brackets
PrintL "Function exit value", Variadic1( (123, 456, 789, Sin(Rnd), Cos(Rnd)), ("ABCDE", "---Z---") )
Up to 32 parameters can be passed to subs/functions.
Getting data out of a function
To get data out of a function, you have direct or indirect way. Here they are.
Direct way
There are 3 possible direct ways to return a value from a function:
1.assign your value or expression to the Function keyword before function ends
2.assign your value or expression to the name of the function you are inside before function ends
3.use Return followed by the value or expression you want to return before function ends
For example, the following function calculates the total number of bytes of all files inside a directory. To return the total number of bytes, the calculated value is assigned to Function keyword before function ends.
USES "FILE" 'Tell thinBasic you want to use "FILE" module
USES "LL" 'Tell thinBasic you want to use "LL" module (Linked List module)
Function TotalFileSize(lDir As String) As Quad
Local Count As Long
Local FileName As String
Local pList As Long
Local nItems As Long
Local TotSize As Quad
pList = Dir_List(lDir, "*.*", %FILE_NORMAL Or %FILE_HIDDEN Or _
%FILE_SYSTEM Or %FILE_ADDPATH)
nItems = LL_Count(pList)
If nItems > 0 Then
For Count = 1 To nItems
FileName = LL_GetItem(pList, Count)
TotSize = TotSize + File_Size(FileName)
Next
End If
'---Here the 3 possible ways to return the value
Function = TotSize '---Method 1
TotalFileSize = TotSize '---Method 2
Return TotSize '---Method 3
End Function
Indirect way
An indirect way to return data from a function is to pass one or more parameter using BYREF option in from of parameter name.
BYREF means you want to pass a reference of a variable instead of the value of the variables.
Changes made inside a function to parameters passed BYREF will change the value of the referenced variable.