New-style functions in gretl ============================ New-style functions are somewhat C-like, in that they have zero or more named parameters (no positional "$" parameters) and can return stuff, and any new variables generated within a function are local to that function and hence are destroyed on exit, unless they are "picked up" (assigned to) by the caller. As in C, function parameters are passed strictly by value: the function gets a local copy of any variable that is passed in; the variable of the same name outside the function is never modified by what goes on inside the function. Stricter than C, code executing within a function cannot "see" any outer variables. Thus if you do "genr x = ..." within a function, any variable named "x" outside of the function is left untouched; and if you do "genr y = x", you'll get an error if the right-hand side variable "x" is undefined within the function (even if there's an "x" in existence outside the function.) So any outer variables needed by a function must be passed in as parameters. The above points are intended to make functions "safe" (properly encapsulated): I can include and call in my own gretl script a function written by someone else and be confident that it won't have any weird side effects (see below for more on this). Different from C, functions can return more than one variable. The caller picks up one or more of the return values with a statement like (r1, r2) = funcname where the variables to which assignment should be made are given first, enclosed in parentheses and separated by commas. Specification of function parameters: The syntax for this is quite flexible. You start the definition of a function with function funcname where is a list of parameters separated by spaces and/or commas, and optionally enclosed in parentheses. Thus the following are all equivalent: function foo x y z function foo x,y,z function foo(x, y, z) The parameters names will be used for local variables, so they must conform to gretl's requirements on variable names. With new-style functions there's a check in place whereby the number of arguments given with a function call must match the number of parameters specified by the function. This could be relaxed in future to allow for optional parameters and/or parameters with default values. Return values: The "return" statement in the definition of a function does not work as in C. It does not cause the function to return at the point where it appears within the body of the function. Rather, it just specifies which variables are available for "grabbing" when the function exits, and a function exits only when (a) the end of the function code is reached, or (b) a "funcerr" statement is reached, or (c) a gretl error occurs. It therefore doesn't matter where the "return" statement is placed within the definition of a function; it can equally well go at the top as at the bottom. This could be relaxed in future, so that a function can return at a specified point. genr modifications Alongside new-style functions, new keywords "series" and "scalar" are introduced as aliases for "genr". (Although these keywords were introduced with functions in mind, they are available regardless of whether or not we're inside a function.) "series": ensures that the variable generated is a vector. If the right-hand side is naturally a scalar, it is expanded into a vector. "scalar": flags a requirement that the right-hand should evaluate to a scalar. If it evaluates to a vector, an error is raised. Even where these keywords are redundant, it is probably a good idea to use them within functions to enhance the comprehensibility of the code. Relevant new capabilities in genr: the function isvector() takes the name of a variable as argument and returns 1 if the variable is a vector, 0 if it is a scalar; the function firstobs() takes the name of a variable as argument and returns the index number of the first non-missing observation for that variable; the function lastobs() works like firstobs() except that it returns the index number of the last non-missing observation. More on encapsulation We've ensured that new-style functions can't modify pre-existing global variables in a script, and can't add new variables without being specifically invited to do so by the caller (via assignment of return values). However, there is another aspect of encapsulation to consider, namely the fate of variables governed by the "set" command. The simplest example of this is the matter of command echo on or off -- one probably wants to set echo off inside a function. Solution: When a function starts executing, the current "set" options are pushed onto a stack; and when the function exits the previous values are popped (restored). A new type: the list With new-style functions, as mentioned above, functions can "see" only those global variables that were passed in as arguments. To handle the case where several variables have to be passed into the function (e.g. a set of regressors) and the number of variables in question is not known in advance, we have introduced a new "list" type, that is, a named list of variables. Such a list may be given as an argument to a function. You create a list with list = where is a space-separated set of elements comprising (a) the names or ID numbers of variables and/or (b) the names of previously defined lists. A valid starts with a letter and has a maximum of 31 characters. If is the name of an already existing list, this command redefines the list in question. You can create an empty list with list Named lists can be used everywhere that gretl expects a list, for example a list of regressors or a list of variables to print: list xlist = x1 x2 x3 ols y 0 xlist print xlist --byobs The new genr function islist() takes a string as argument and returns 1 if it is the name of a saved list, 0 otherwise; the function nelem() takes the name of a list as argument, and returns the number of elements in that list (or NA if the given string is not in fact the name of a list). A few examples of what one can do: # create a list of specified lags of x scalar m = 4 list xlags = x(0 to -m) # create a list of logs list foo = x1 x2 x3 list bar = logs(foo) The functions logs(), diff(), ldiff() and square() operate on named lists, as in the last example above, to produce a list of transformed variables. More examples: # simple loop across elements of a named list list foo = x1 x2 x3 loop foreach i foo ols y const $i endloop list foo = x1 x2 x3 # make an empty list list bar loop foreach i foo ols y const $i --quiet scalar r2 = $rsq if (r2 < 1) # add member of list foo to list bar list bar = bar $i endif endloop Allin June, 2005