Functions

Here, we discuss the functions, an important means of organizing program logics.

- Table of Contents -

Functions

For example, suppose you want to perform the operation "a * a + b * b" for various numbers "a" and "b" in multiple places in your program.

In such cases, it's possible to write "a * a + b * b" in all the necessary places, but that's a bit cumbersome. Especially if later you want to change it to something like "a * a + b * b - 2", you'll have to rewrite it in multiple places.

In situations like this, it's convenient to define a function beforehand. A function is a mechanism for receiving a set of variables, performing operations on them, and outputting the result.

Functions are defined in the following form:

return_type   function_name( parameter_declaration1, parameter_declaration2, c ) {

    // Process

    return return_value;
}

Here, "parameter" (or "formal parameter", sometimes "formal argument") refers to the variables declared as part of the function definition, for receiving values for processing. When a function is called, values passed to the function's parameters are referred to as "actual arguments".

Also, a "return value" refers to the value that the function outputs as the result of its processing.

Example of a Function Processing "a * a + b * b"

As an example, let's define a function that returns the value of "a * a + b * b" for arbitrary integers "a" and "b":

DefAABB.vcssl

This function takes integers a and b as the parameters, and calculates "a * a + b * b", assigning the result to a variable "value" internally, then returns it as the return value.

Now, let's call this function from within a program.

To call a function from within a program, simply write the function name followed by parentheses. Inside those parentheses, you specify the actual arguments separated by commas.

The following code demonstrates how to call this function:

DefAABB.vcssl

- Execution Result -

5

Executing this code displays 5. This confirms that "1 * 1 + 2 * 2" was correctly executed inside the function fun and returned as the return value.

Using "const" in Parameter Declarations to Make Them Immutable

By the way, in regular variable declarations, you can make them immutable, or "constants," by prefixing them with the keyword "const." Similarly, in parameter declarations, you can make them immutable by prefixing them with "const." Doing so not only prevents accidental modifications but also facilitates optimization.

In the above sample code, since parameters a and b don't need to be modified, you can declare them with "const" as follows:

DefAABBConst.vcssl

The behavior is exactly the same as before, but attempting to modify a or b inside the function will result in an error.

Functions That Do Not Return Anything

Functions don't necessarily have to return a value. Functions that do not return a value are declared with the "void" type.

VoidReturns.vcssl

Functions Without Parameters

Functions can also have no parameters. In such cases, you leave the parentheses empty.

VoidArgs.vcssl

Functions with Array Parameters

You can declare arrays as parameters for functions as well. This is done as follows:

ArrayArgs.vcssl

Alternatively, in VCSSL, you can also write it like this, with [] attached to the data type:

ArrayTypeArgs.vcssl

With this, you can receive integer arrays "a" and "b" as parameters. The size of these arrays will be determined by the actual array arguments passed in the program calling the function. If needed, you can check the number of elements using the length(array[], int dim) function.

When passing arrays like this, it's a pass by value. In other words, all elements in the actual array arguments are copied to the array parameters. Therefore, if you modify the parameter values inside the function, the change will not be reflected in the array of the actual argument passed from the caller-side.

Compatibility with Other C-like Languages

In many other C-like languages, if you modify the contents of an array parameter inside a function, those modifications are reflected in the array passed from the calling code (actual argument). This is because in many languages, passing an array as an argument involves passing the address/reference of the array data.

On the other hand, as mentioned ealier, in VCSSL, array argument are passed by copying values by default, as same as non-array arguments. Therefore, modifications made to the array parameters inside the function will not be reflected in the array passed from the calling code. If you want the changes to be reflected, you need to explicitly declare the array parameter as passed by reference (we'll explain how to do this later).

This difference is particularly important to note in terms of compatibility with other C-like languages.

Functions Returning Arrays

In VCSSL, functions can also return arrays. To do this, declare the function's return type with [] as follows:

ArrayReturns.vcssl

With this, you can return an array as the return value. When calling this function from within a program and assigning the return value to an array, the size of the array will automatically match the returned array's size.

The following code is an example returning an array:

ArrayReturnsAndAssignment.vcssl

In the above code, the return value of fun(2,3) is assigned to an array variable "m". The size of m is adjusted dynamically when the return value is assigned, so we can omit to declare the size of "m", as [].

Caution for C Language Users: Don't Return Arrays from Functions in C Programs
In the C language, you must not return an array statically defined within a function as a return value, as it causes undefined behavior due to the local scope of the array which is deallocated once the function returns. However, in some languages, including VCSSL, it is possible to return a locally defined array from a function.

Pass by Value and Pass by Reference

If you declare function parameters simply as shown in the examples so far, they will be passed by value, meaning the value is copied (assigned) when called.

In other words, even if you modify the value of a parameter inside the function, it won't be reflected in the calling code.

On the other hand, if you prefix the variable name of a parameter with the ampersand symbol "&", it will be passed by reference, meaning changes will be reflected in the calling code:

CallByRef.vcssl

- Execution Result -

i=0   j=1

In the example above, "i=0 j=1" will be displayed, showing that the change in j was reflected through pass by reference.

Pass by Reference for Arrays

You can also pass array arguments by reference. In such cases, you declare the array name of the parameter with the ampersand symbol "&" prefixed as follows:

CallByRefArray.vcssl

However, as mentioned below, pass by reference has some considerations, limitations, and drawbacks, even for arrays.

Considerations for Pass by Reference

Passing by reference involves not the value of the data itself, but rather the 'location (address)' where the data resides, which entails several constraints and points of caution.

Cases Where Pass by Reference Is Not Possible

Pass by reference cannot always be done and there are situations where it's not possible.

For example, you cannot directly receive the return value of another function as a reference parameter's actual argument. In such cases, first, store the function's return value in a variable, and then pass this variable by reference.

Special Attention Needed for Pass by Reference of Array Elements

Another point to note is when passing array elements by reference.

For instance, consider a function "fun" that accepts an argument by reference. If you pass an array element to it by reference using "fun(a[i])", it's crucial to avoid changing the index variable "i" from outside the function until its processing completes. For example, if "i" was 2 when calling "fun(a[i])", you should not change "i" externally to 3 while the function is still running. Doing so will result in undefined behavior.

Similarly, when passing array elements by reference, you should not reallocate the array using "alloc" functions until the function's processing is complete. Again, doing so will result in undefined behavior.

In essence, when passing array elements by reference, you must be cautious to avoid scenarios where the address of the element changes or the reference path becomes ambiguous during processing. This restriction is analogous to the concept of not changing your address while processing documents that require it.

Pros and Cons of Pass by Reference

Operations on parameters passed by reference can often be subject to optimization constraints, potentially slightly reducing processing efficiency. Therefore, it is generally advisable to avoid using pass by reference for functions that involve heavy computational tasks and frequently operate on the received values.

However, when the actual argument passed is an array of considerable size, passing by value introduces the overhead of copy operations, which can become a processing bottleneck. In contrast, the cost for passing by reference remains constant, offering a significant advantage over passing by value in such scenarios.

Furthermore, the excessive use of pass by reference, especially when passing arguments by value and returning results via return values would be sufficient, can impair the overall readability of the program. Generally, modifying the values of arguments passed by reference is not recommended from a readability standpoint.

When considering pass by reference, it's crucial to balance these pros and cons to determine whether it is indeed the best approach for your specific scenario.

Acknowledgement: We greatly appreciate the cooperation of two ChatGPT AIs in translating this page.
» How we translated this page