Getting Started | Documentation | Glish | Learn More | Programming | Contact Us |
Version 1.9 Build 1556 |
|
Each function definition includes zero or more formal parameters, enclosed within ()'s. Each formal looks like:
type name = expressiontype and = expression are optional. (formal's have one other form, ``...", discussed in § 6.4.4, page .)
name serves as the name of a local variable that during a function call is initialized with the corresponding actual argument. (See § 6.6.1, page , for a discussion of local variables.) As in most programming languages, actual arguments match with formal parameters left-to-right:
function diff(a, b) a-b
...
diff(3, 7)matches 3 with a and 7 with b. Argument matching can also be done ``by name'':
diff(b=1, a=2)matches 1 with b and 2 with a.
If the function definition formal includes an = expression, an actual argument for that formal parameter can be left out when calling the function. The actual argument is instead initialized using expression (expression is referred to as the formal's default). As you saw above, you can define diff as:
function diff(a, b=1) a-bin which case a call with only one argument will match that argument with a and initialize b to 1. A call using by-name argument matching, though, can not specify b and not a, because a has no default:
diff(b = 3)is illegal.
You can instead define diff with:
function diff(a=0, b) a-bso that only b is required in a call. This makes diff a ``negation'' function. A call like:
diff(6)is now illegal, because 6 matches a and not b; but the call
diff(b = 6)is legal and returns -6. Arguments which have defaults can simply be left out, as long as their absence is denoted. The previous invocation can also be written as:
diff(,6)The first argument is purposefully left out, therefore it is assigned the default value. You can test for missing arguments using the missing() function. (See § 6.5, page .)
Note that while match-by-position and match-by-name arguments can be intermixed, a parameter must be specified only once. For example,
diff(3, 4, a=2)is illegal because a is matched twice, first to 3 and then to 2. Furthermore, once a match-by-name argument is given no more match-by-position arguments can be given, because their position is indeterminate:
diff(a = 3, 2)is illegal, since it's unclear what parameter 2 is meant to match.
A formal parameter definition can also include a type. Presently, the type is one of ref, const, or val. The type indicates the relationship between the actual argument and the formal parameter.
If the formal parameter's type is ref then the formal is initialized as a reference to the actual argument. In this case, the actual argument (outside of the function) can be modified with the ref parameter. (See § 4.6, page regarding val assignment and § 3.8, page , for a full discussion of references.)
The val parameter type indicates that the parameter can be modified, but changes won't be reflected in the actual parameter which is passed into the the function. The default type for parameters is val.
If the type is const, then it's initialized as a const value. A const parameter cannot be modified in the course of executing the function. Attempts to modify const parameters result in errors (See § 3.9, page for a discussion of const values).
function bump(ref x) { val x +:= 1 }After executing:
y := 3 bump(y)y's value is 4. Note though that the following call:
bump(3)is perfectly legal and does not change the value of the constant 3 to 4!
Here's another example of using a ref parameter:
# sets any elements of x > a to 0. func remove_outliers(ref x, a) { x[x > a] := 0 }
This same mechanism is used when passing parameters or returning values. The default parameter type is val, but the parameter is only really copied if the parameter is modified in the function. So large parameters are only copied if necessary. Using a const parameter type provides a way to ensure that a given parameter does not change and, as a result, is not copied. With return values, the value being returned is never modified after the return statement, and as a result, return values are not copied in the process of returning from the function.
function abs(val numeric x)In this case if abs is called with a non-numeric value Glish detects the type clash and generates an error.
You can write functions that take a variable number of parameters by including the special parameter ``..." (called ellipsis) in the function definition. For example, here's a function that returns the sum of all its arguments, regardless how many there are:
func total(...) { local result := 0 for ( i in 1:num_args(...) ) result +:= nth_arg(i, ...) return result }
Two functions are available for dealing with variable argument lists. The function num_args returns the number of arguments with which it is called. The nth_arg function returns a copy of the argument specified by its first argument, with the first argument numbered as 0. For example,
num_args(6,2,7)returns 3 and
nth_arg(3, "hi", 1.023, 42, "and more")returns 42.
There's a temptation to expect num_args and nth_arg to return information about ``..." if they're not given an argument list, but presently they do not. Probably they will be changed to do so in the future.
Note that the only operation allowed with ``..." is to pass it as an argument to another function. It cannot otherwise appear in an expression. When passing it to a function, it is expanded into a list of const references to the actual arguments matched by the ellipsis. For example,
func many_min(x, ...) { if ( num_args(...) == 0 ) return x else { ellipsis_min := many_min(...) if ( ellipsis_min < x ) return ellipsis_min else return x } }returns the minimum of an arbitrary number of arguments.
When an ellipsis is used in a function definition then any parameters listed after it must be matched by name (or by default). Furthermore, the corresponding arguments must come after those to be matched by the ellipsis. For example, given:
func dump_ellipsis(x, ..., y) { for ( i in num_args(...) ) print i, nth_arg(i,...) }both of the following calls are illegal:
dump_ellipsis(1, 2, 3) dump_ellipsis(1, y=2, 3)In the first y is not matched, and in the second the actual argument 3 is not matched (in particular, it is not matched by the ellipsis). The following, though, is legal:
dump_ellipsis(1, 2, y=3)and results in the ellipsis matching the single argument 2.
An ellipsis can also have a default value specified. This value is used as the value for any arguments which are purposefully left out. In the following,
func add(...=0) { local ret := 0; for ( i in num_args(...) ) ret +:= nth_arg(i,...) }add is defined so that any arguments purposefully left out will be set to zero. So the following invocation,
print add(1,2,,,5)prints 8. The two arguments between the 2 and the 5 default to 0.
An ellipsis can be used to construct a vector. This allows all of the parameters to be captured as a vector:
func args(...) { return [...] }this returns the parameters as a vector. So given the following invocations,
args(1,5,8) args(4,3:5,1)the result of the first is [1, 5, 8], and the result of the second is [4, 3, 4, 5, 1]. It is important to note, however, that if one of the arguments is not an array value, e.g. a record, an error results.