Another long section explaining
EULER would not be as powerful as it is, if there wasn't the possibility to extend it with user defined functions. A function can be entered from the keyboard or better from a file. Since there may be errors in a function, it is best to call an editor, edit the function in a file and then load that file.
Loading a file is done with
>load "filename"
A function is declared by the following commands
>function name (parameter,...,parameter) >... >endfunction
It can have several parameters or none. If the function is entered from the keyboard, the prompt changes to "$". "endfunction" finishes the function definition. An example
>function cosh (x) $ return (exp(x)+exp(-x))/2 $endfunction
Every function must have a return statement, which ends the execution of the function and defines the value it returns. A function can be used in any expression, just as the built-in functions. If a function is not used in an assignment and with surpressed output (followed by ";"), it is used like a procedure and its result is evidently lost. However, the function may have had some side effect.
A function can call itself recursively. In this case, you have to take care that the recursion stops. If a function overrides an internal function, you can call the internal function with an underscore _ before its name. Thus a function is able to call the internal function, even if it has the same name. This is useful to give enhanced flexibility to built-in functions.
Inside a function one cannot access global variables or variables of the function which called the function. To use global variables use the command
>global variablename
in the function body. Of course, one can use other functions in expressions inside a function, one can even use the function inside itself recursively. All variables defined inside a function are local to that function and undefined after the return from the function. There is no way to define global variables or change the type or size of global variables from within a function, even if one defines these variables in a "global" statement.
>useglobal
lets you access all global variables from inside the function. This is only valid for the function containing the useglobal command.
Variables are passed as references.
That means that a change of a parameter results in the change of the variable, which was passed as the parameter. For example
>function test(x) $ x=3; $ return x $endfunction >a=5; >test(a); >a
prints the value 3. There is an exeption to this. A submatrix is passed by value. Thus
>a=[1,2,3]; >test(a(1)); >a(1)
prints the value 1.
A function can have a variable number of parameters. The number of parameters passed to a function can be determined with the built-in function "argn()". If the parameters are not named in the function definition, they are given the names arg1, arg2 and so on.
You can access the parameters from the n-th parameter on with args(n). This functions returns multiple values (argn,...) and these values may be passed to another function as several parameters. pargs() will return all parameters from the first non-named parameter. These additional parameters may be passed to a function. If the parameter list in the function call contains a semicolon ";", args() will return all parameters after the semicolon.
Parameters can have default values. The syntax is parameter=value in the parameter definition; e.g.,
>function f(x=3,y,z=1:10)
assigns the default value 3 to x and the vector 1 to 10 to z, if the function is called in the form
>f(,4,)
If the function is called
>f(1,4)
x has the value 1, y the value 4, and z the value 1:10.
The function can even be given a named parameter. Consider the function
>function f(x,y,z=4,w=4,t=5)
Then
>f(1,2,t=7)
calls the function as if
>f(1,2,4,4,7)
was entered. Actually the name needs not be a parameter name. Thus
>f(1,2,s=7)
defines a local variable s with value 7.
A function can return multiple values. This is done with the return statement
> return {x,y,...}
You can assign all the return values of a function to variables, using
>{a,b,...}=function(...);
If the result of such a function is assigned to a number of variables smaller than the number of returned values, only the first values are used. If is is assigned to a larger number of variables, the last value is used more than once. Some built-in functions return multiple values.
Comments can be included, starting with ##. Comments are ignored by Euler and do not appear, when you type the function body with
>type functionname
If the lines immediately after the function header start with ##, then those lines are considered to be help text. The help text can be displayed with
>help functionname
This feature is a good way to remember the parameters of the function. Also
>list
can be used to display a list of the names of the user defined functions.
A function or several functions can be removed with
>forget name,...
By the way, a variable can be removed with
>clear name,...
Like in any other programming language, EULER has commands for changing the execution flow. These commands can only be used inside user defined functions.
First there is the "if" command.
>if expression; ...; >else; ....; >endif;
The expression is any EULER numerical expression. If it is a non-zero scalar ora a matrix with all entries different from zero, then the part from the ";" to the "else;" is evaluated. Else the part from "else;" to "endif" is evaluated. Of course "..." may spread over several lines. To work correctly, keywords like "if", "else", "endif" and others should be the first nonblank characters in a line, or should be preceded by "," or ";" (plus blanks or TABs). The "else" may be omitted. In this case the evaluation skips behind the "endif", if the matrix contains zero elements (resp. the number is zero).
You may also include one or several elseif commands
>if expression; ...; >elseif expression; ...; >else; ....; >endif;
This is the same as an if command inside the else part. However, you do not have to type several endifs.
There is the function "any(A)", which yields 1, if there is any nonzero element in A, 0 otherwise. The function is useful in connection with the if statement.
Next, there are several loops.
>repeat; ...; end; >loop a to b; ...; end; >for i=a to b step c; ...; end;
All loops can be aborted by the break command (usually inside an "if"), especially the seemingly infinite "repeat". "loop" loops are fast long integer loops. The looping index can be accessed with the function "index()" or with "#". In a "for" loop the looping index is the variable left of the "=". The step size can be omitted. Then it is assumed to be 1. As an example, the following loops count from 1 to 10
> i=1; > repeat; > i, i=i+1; > if i>10; break; endif; > end;
and
> loop 1 to 10; #, end;
and
> for i=1 to 10; i, end;
The command
>trace on
sets tracing of all functions on. Then any new line in a user defined function will be printed with the function name before it is executed. The uses has to press a key, before execution continues
Any other key will display the available keys. Note that the help key is mapped to the insert key on a PC.
>trace off
switches tracing off.
Note, that with F4 you can evaluate any expression, even if it contains local variables or subroutine calls. Tracing is switched off during evaluation of the expression.
A single function can be traced with
>trace function
or
>trace "function"
Execution will stop only in this function. The same command switches the trace bit of this function off.
>trace alloff
switches tracing for all functions off.
All internal EULER functions can handle vector or matrix input. And so should user defined functions. To achieve this, sometimes nothing special needs to be done. E.g., the function
>function kap (r,i,n) > p=1+i/100; > return p*r*(p^n-1)/(p-1) >endfunction
is automatically capable to handle matrix intput. Thus
>kap(1000,5:0.1:10,10)
will produce a vector of values. However, if the function uses a more complicated algorithm, one needs to take extra care. E.g.,
>function lambda1 (a,b) > return max(abs(polysolve([1,a,b,1]))); >endfunction >function lambda (a,b) > return map("lambda1",a,b); >endfunction
shows the fastest way to achieve the aim.
Forthermore, as a matter of good style, one should use the help lines extensively. All parameters of the function and its result should be explained. This is a good way to remember what the function really does.
It is possible to pass functions to a function. E.g., the function "bisect" in UTIL finds the zero of a function using bisection. This function works in the following way
>function f(x) > return x*x-2 >endfunction >bisect("f",1,2)
The result will be equal to sqrt(2). If "f" needs extra parameters, those can also be passed to "bisect"
>function f(x,a) > return x*x-a >endfunction >bisect("f",0,a,a)
will result is equal to sqrt(a) (for a>=0). The search interval is set to [0,a].
The way to write a function like "bisect" is to use the "args" function.
>function bisect (function,a,b) >... > y=function(x,args(4)); >... >endfunction
Then "function" will be called with the parameter "x" and all parameters from the 4-th on (if any) which have been passed to "bisect". Of course, "function" should be assigned a string, containing the name of the function which we want the zero of.
The function bisect does also accept an expression in the variable "x". The programmer can use
>evaluate(expression)
to evaluate an expression. The variable "x" should be defined either globally (then useglobal must be used), or in the function bisect itself. To determine, if a string is a function or an expression, the function isfunction("f") may be used.
Another way to achieve this result is the use of args() without parameter. This will return all parameters from the first additional parameter on. If the user calls bisect like this
>bisect ("f",a,b;4,5)
All parameter after the ";" will be passed to function, when it is called
>y=function(x,args());
Clearly, EULER is designed to run interactively. The user loads a file containing the functions he needs. The file may inform the user of its content by simply printing some messages with commands like
>"The content of this file is:", >...
Alternatively, the file could contain a comment section.
comment Any comment! Even in several lines. endcomment
Then the user can use the help facility to retrieve further information on the functions, its parameters and so on. He (or she) then calls the particular function with the parameters he desires.
However, it is also possible to run a file as a standalone program. If you start EULER from a shell simply put the file into the command line.
If you wish a standalone application, the user will have to enter data. You can prompt him with
>data=input("prompt");
The prompt
prompt? >
will appear and the user may enter any valid EULER expression, even if it uses variables. Errors are catched and force the user to reenter the input. If you wish the user to enter a string, use
>string=lineinput("prompt");
The string may then be evaluated with
>data=interpret(string);
and if it does not consist of a valid EULER expression the result is the string "error". Also
>errorlevel(string)
returns a nonzero number, if there is an error in the string.
Output is printed to screen. All expressions and assignments produce output unless followed by ";". If formated output is wanted, use
>printf("formatstring",realdata)
The format string obeys the C syntax; e.g., "%15.10f" prints the data on 15 places with 10 digits after decimal dot, and "%20.10e" produces the exponential format. You can concatenate strings with | to longer output in a single line.
Output is surpressed globally with
>output off;
and
>output on;
turns the output on again. This is useful if a dump file is defined by
>dump "filename";
Then all output is echoed into the dump file. The command
>dump;
turns the dump off. Note that the dump is always appended to the file. Furthermore, that file may not be edited while dump is on! The utility function
>varwrite(x,"x")
is defined, which writes x in a format readable by EULER on input. If you omit the name "x", the name of x is used automatically. This is done with the help of the function
>name(x)
which is a string containing the name of x.
>cls;
clears the screen.
>clg;
clears the graphics. Also to show graphics to the user, use
>shg;
Subsequent output will switch back to the text screen.
Finally, an error can be issued with the function
>error("error text")
One can make all sorts of mistakes in EULER. This section tries to warn you of the more common ones, most of which the author has some experience with.
As already mentioned, you should not assign to the parameter of a function. This will generally produce strange errors, which are difficult to debug.
The next mistake is to produce matrices with 0 rows or columns. EULER can not do any computation with these matrices. Make sure that every index you use is in range. And use special handling, if there is nothing to do. However, you may generate such matrices for the special purpose of appending vectors to it.
Another subtlety concerns the use of multiple return values. The following simply does not work
>x=random(1,10); sin(sort(x))
The reason is that sort returns not only the sorted array but also the indices of the sorted elements. This works as if sin was passed two parameters and EULER will not recognize that use of sin. To work around this either assign the sorted array to a variable or put extra brackets around it
>x=random(1,10); sin((sort(x)))
Also a return statement like
>return {sort(x),y}
really returns 3 (or more) values! Use
>return {(sort(x)),y}
One further misfortune results from the use of strings as functions, like in
>function test(f,x) > return f(x*x) >endfunction >test("sin",4)
This works well as long as there is no function by the name of "f". If there is, this function is called rather than the sine function. The only way to avoid this is to use really strange names for function parameters. I prefer "test(ffunction,x)" and used it throughout UTIL.
Finally, I like to stress that the user defined functions are serached before the biult-in functions. If you want to call the built-in function explicitely, you can prepend it with an underscore "_".