Functions
In a model, computations must be performed to process the information that is sent around. Short and simple calculations are written as assignments between the other statements, but for longer computations or computations that are needed at several places in the model, a more encapsulated environment is useful, a function. In addition, the language comes with a number of built-in functions, such as size
or empty
on container types. An example:
func real mean(list int xs):
int sum;
for x in xs:
sum = sum + x
end;
return sum / size(xs)
end
The func
keyword indicates it is a function. The name of the function is just before the opening parenthesis, in this example mean
. Between the parentheses, the input values (the formal parameters) are listed. In this example, there is one input value, namely list int
which is a list of integers. Parameter name xs
is used to refer to the input value in the body of the function. Between func
and the name of the function is the type of the computation result, in this case, a real
value. In other words, this mean
function takes a list of integers as input, and produces a real
value as result.
The colon at the end of the first line indicates the start of the computation. Below it are new variable declarations (int sum
), and statements to compute the value, the function algorithm. The return
statement denotes the end of the function algorithm. The value of the expression behind it is the result of the calculation. This example computes and returns the mean value of the integers of the list.
Use of a function (application of a function) is done by using its name, followed by the values to be used as input (the actual parameters). The above function can be used like:
m = mean([1, 3, 5, 7, 9])
The actual parameter of this function application is [1, 3, 5, 7, 9]
. The function result is (1 + 3 + 5 + 7 + 9)/5
(which is 5.0
), and variable m
becomes 5.0
.
A function is a mathematical function: the result of a function is the same for the same values of input parameters. A function has no side-effect, and it cannot access variables outside the body. For example, it cannot access time
(explained in Servers with time) directly, it has to be passed in through the parameter list.
A function that calculates the sign of a real number, is:
func int sign(real r):
if r < 0:
return -1
elif r = 0:
return 0
end;
return 1
end
The sign function returns:
-
if
r
is smaller than zero, the value minus one; -
if
r
equals zero, the value zero; and -
if
r
is greater than zero, the value one.
The computation in a function ends when it encounters a return
statement. The return 1
at the end is therefore only executed when both if
conditions are false.
Sorted lists
The language allows recursive functions as well as higher-order functions. Explaining them in detail is beyond the scope of this tutorial, but these functions are useful for making and maintaining sorted lists. Such a sorted list is useful for easily getting the smallest (or largest) item from a collection, for example the order with the nearest deadline.
To sort a list, the first notion that has to be defined is the desired order, by making a function of the following form:
func bool decreasing(int x, y):
return x >= y
end
The function is called predicate function. It takes two values from the list (two integers in this case), and produces a boolean value, indicating whether the parameters are in the right order. In this case, the function returns true
when the first parameter is larger or equal than the second parameter, that is, larger values must be before smaller values (for equal values, the order does not matter). This results in a list with decreasing values.
The requirements on any predicate function f
are:
-
If
x != y
, eitherf(x, y)
must hold orf(y, x)
must hold, but not both. (Unequal values must have a unique order.) -
If
x == y
, bothf(x, y)
andf(y, x)
must hold. (Equal values can be placed in arbitrary order.) -
For values
x
,y
, andz
, iff(x, y)
holds andf(y, z)
holds (that isx >= y
andy >= z
), thenf(x, z)
must also hold (that is,x >= z
should also be true).
(The order between x
and z
must be stable, even when you compare with an intermediate value y
between x
and z
.)
These requirements hold for functions that test on <=
or >=
between two values, like above.
If you do not provide a proper predicate function, the result may not be sorted as you expect, or the simulator may abort when it fails to find a proper sorting order.
Sort
The first use of such a predicate function is for sorting a list. For example list [3, 8, 7]
is sorted decreasingly (larger numbers before smaller numbers) with the following statement:
ys = sort([3, 8, 7], decreasing)
Sorting is done with the sort function, it takes two parameters, the list to sort, and the predicate function. (There are no parentheses ()
behind decreasing
!) The value of list ys
becomes [8, 7, 3]
.
Another sorting example is a list of type tuple(int number, real slack)
, where field number
denotes the number of an item, and field slack
denotes the slack time of the item. The list should be sorted in ascending order of the slack time. The type of the item is:
type item = tuple(int number, real slack);
The predicate function spred
is defined by:
func bool spred(item x, y):
return x.slack <= y.slack
end
Function spred
returns true
if the two elements are in increasing order in the list, otherwise false
. Note, the parameters of the function are of type item
. Given a variable ps
equal to [(7, 21.6), (5, 10.3), (3, 35.8)]
. The statement denoting the sorting is:
qs = sort(ps, spred)
variable qs
becomes [(5, 10.3), (7, 21.6), (3, 35.8)]
.
Insert
Adding a new value to a sorted list is the second use of higher-order functions. The simplest approach would be to add the new value to the head or rear of the list, and sort the list again, but sorting an almost sorted list is very expensive. It is much faster to find the right position in the already sorted list, and insert the new value at that point. This function also exists, and is named insert
. An example is (assume xs
initially contains [3,8]
):
xs = insert(xs, 7, increasing)
where increasing
is:
func bool increasing(int x, y):
return x <= y
end
The insert
call assigns the result [3,7,8]
as new value to xs
, 7
is inserted in the list.