Let me introduce you into the programming language of EULER. Our first example is a simple one line function. Such a function is defined by the "function" keyword, the function name with variables, a ":=", and an expression.
>function f(x) := x^3+x^2-x
The function can be used in any numerical expression, just like the built-in functions.
>f(5)+f(f(3))
37138
You can also define a symbolic function, which will work in symbolic expressions too. Of course, this is only possible, if the function uses a simple expression, and if this expression can be evaluated by Maxima.
>function f(x) &= x^3+x^2-x
3 2 x + x - x
In fact, the evaluation of symbolic expressions takes place at compile time. The right hand side will be evaluated before the function is defined, as you can see in the following example.
>function df(x) &= diff(x^3+x^2-x,x)
2 3 x + 2 x - 1
The function will work for vectors x, since the Euler matrix language takes care of matrix values in expressions automatically, and this function uses only a simple expression.
>f(1:10)
[ 1 10 33 76 145 246 385 568 801 1090 ]
Symbolic functions and expressions can be used whenever expressions are expected. They can be used in a very simple way, just by their name.
>plot2d(f):
The same function can also be defined in multiple lines. Then the function definition must end with a line containing "endfunction" The function can contain a return statement, or else it will return the string value "none". To enter such a function, its lines can be entered one by one, until endfunction finishes this input mode (press escape to put "endfunction" in an empty line). Or you can pess F9 in the function line and use the internal editor to edit the function.
>function f(x)
return x^3+x^2-x endfunction
Here is the same multi-line function with ... in the first line. Then the function can be defined with only one return in the "function" line.
>function f(x) ...
return x^3+x^2-x endfunction
To edit such a multi-line function, go to the "function" line and press F9. The function works for real numbers.
>f(2)
10
It also works for complex numbers.
>f(I)
-1-2i
And even for intervals.
>f(~2,3~)
~9,34~
Likewise for vectors.
>f(1:5)
[ 1 10 33 76 145 ]
But not for strings.
>f("test")
Wrong argument. Cannot use a string here. Error in ^ Error in function f Error in : return x^3+x^2-x ^ Error in function f
To make sure, the function is not abused, we can fix its parameter type as follows.
>function f(x:numerical) := x^3+x^2-x
Now, we get a meaningful error message.
>f("test")
Function f needs a numerical type for x Error in : f("test") ^
There are many more parameter types to help error checking. real, complex, interval, integer: include scalars, vectors or matrices scalar: no vector, no matrix vector: row vector, includes scalar column: column vector, includes scalar numerical: not a string or compressed matrix string: excludes vector of strings cpx: compressed matrix positive, nonnegative: additional hint You can combine some of these types. Here are some predefined combinations. number: real scalar natural: nonnegative integer index: positive integer scalar indices: positive integer vector We can also do error checking with typeof(...) and error(...).
>function myprint (s, n:index) ...
if typeof(s)==8 then return printstr(s,n); elseif typeof(s)==0 then return printstr(""+round(s,n-5),n); else error("Cannot print this"); endfunction
The types are numbers. 8 is for strings.
>myprint("Affe",10)
Affe
A real is of type 0.
>myprint(pi,10)
3.14159
In all other cases we get an error.
>myprint(I,10)
Error : Cannot print this Error generated by error() command Error in function myprint
The following function demonstrates how to return multiple values from a function. (We explain the "if" statement in the next section).
>function ordertwo (a:number, b:number) ...
if a<b then return {a,b} else return {b,a} endif; endfunction
To assign multiple values, you use the same {...} syntax.
>{a,b}=ordertwo(4,2); [a,b]
[ 2 4 ]
Multiple return values by default do not count for multiple parameters for another function. The sort() function returns two values, the sorted vector and the indices of the sorted elements. Only the first value is used in the call to plot2d.
>plot2d(cumsum(sort(random(1,10))),>bar):
If a function has the "args" modifier as in function args histo (v) ... its multiple returns can be used as parameters in other functions. So we can easily use the multiple result of histo(v) to plot a histogram. The function histo(v) returns {x,y} suited for bar plots.
>plot2d(histo(random(1,100),v=0:0.1:1),>bar):
Of course, it might be better to assign the result to variables first.
>{x,y}=histo(normal(1,1000),20); plot2d(x,y,>bar):
The Euler programming language has the usual control statements. Control statements change the flow of execution. Control statements cannot be used in one-line functions. The loop with a named variable and an optional increment (step ...) is one of them.
>function test ...
for i=1 to 5; i, end; endfunction
Since we are not interested in the return value, there is no return statement in this function. Moreover, the function does not have parameters.
>test;
1 2 3 4 5
It should be mentioned that loops work outside of functions in the command line too, as long as the complete loop fits within a single line.
>for i=1 to 5; i, end;
1 2 3 4 5
The "for" loop can have a step value.
>for i=5 to 1 step -1; i, end;
5 4 3 2 1
And it can loop over a vector.
>v=random(1,5); for x=v; x, end;
0.967662065407 0.502805240796 0.415036068893 0.861652811636 0.904019588065
The following function definition does the same with "repeat".
>function test ...
n=1; repeat n, n=n+1; if n>5 then break; endif end endfunction
"repeat" is an infinit loop. It can be aborted with the "break" command, which jumps to the first command after the end of the loop. We put the "break" into an "if" clause, testing for n>5. Note, that each "if" clause must end with "endif". On the other hand, loops end with "end".
>test;
1 2 3 4 5
Instead of the "break" inside the conditional statement, Euler has "until" and "while". Both statements must be inside a "repeat" loop. The loop must still end with "end".
>function test ...
n=1; repeat n, n=n+1; until n>5; end endfunction
The "while" statement reverses the condition.
>function test ...
n=1; repeat while n<=5; n, n=n+1; end endfunction
>test
1 2 3 4 5
Here is a definition of the modulus function, using an else clause. Of course, it is already contained in Euler as "abs".
>function test (x:number) ...
if x<0 then return -x; else return x; endif endfunction
>test(-1), test(1),
1 1
If can also have an elseif clause. So you do not need to put one if inside the other.
>function test (x:number) ...
if x>0 then "positive", elseif x<0 then "negative", else "zero", endif; endfunction
>test(-1), test(0), test(1),
negative zero positive
The function in the previous example would not work for vectors! We have made the function type safe by requiring x to be a scalar with the "number" keyword. To make the function work for vectors and matrices too, we can either map it at runtime, or define the function with the "map" keyword. At runtime, the function can be forced to map by appending "map" to the function name.
>testmap(-1:1)
negative zero positive
At compile time, we use "function map ...". The following example returns the signum absolute value of a number. (Of course, there is a function "abs" in Euler already.)
>function map test (x:number) ...
if x<0 then return -x; else return x; endif endfunction
>test(-1:1)
[ 1 0 1 ]
It is possible to prevent mapping using a semicolon in the parameter list when the function is defined. In the following example, we do not want to map to the coefficients of the polynomial p.
>function map test (x; p) := evalpoly(x,p)
The function maps only to the first argument. We evaluate
in the following example.
>test(0:0.2:1,[1,2,1])
[ 1 1.44 1.96 2.56 3.24 4 ]
But the builtin function evalpoly() does already obey the matrix language of Euler, and maps to the second argument. So we do not need a function like test(x,p) in this case.
>evalpoly(0:0.2:1,[1,2,1])
[ 1 1.44 1.96 2.56 3.24 4 ]
By default, functions cannot see global variables. The command "useglobal" inside a function, however, makes all global variables visible. This command is inserted automatically for one-line functions.
>function f(x) := a*x
We can type the internal commands of a function using "type".
>type f
function f (x) useglobal; return a*x endfunction
The function can access global variables.
>a:=55; f(2)
110
Alternatively, a global variable, or several global variables, can be selected.
>function f(x) ...
global a; return a*x; endfunction
A global variable can be set to be visible in all functions.
>aglob=55; setglobal aglob; >function f(x) ...
return aglob*x endfunction
aglob is found, even though there is no useglobal command.
>f(2)
110
Using global variables is not a good programming style. It is better to pass the variable as a parameter.
>function f(x,a) := a*x >f(2,55)
110
In Euler, one can provide default values for parameters. The following function computes norms of a row vector.
>function pnorm (v:vector, p:nonnegative number=0) ...
if p>=1 then return sum(abs(v)^p)^(1/p); else return max(abs(v)) endif; endfunction
If p=0 (the default), norm computes the maximum norm.
>v=[2,3,-2]; pnorm(v)
3
Or the Euclidean norm for p=2.
>pnorm(v,2)
4.12310562562
Default parameters can also be set using asigned parameters.
>pnorm(v,p=1)
7
A way to use default parameter is with the default value "none". This is done in many Euler functions to be able to check, if the user has set this parameter or not.
>function multmod (a:integer, b:integer, p:index=none) ...
if p==none return a*b else return mod(a*b,p) endif; endfunction
>multmod(7,9)
63
>multmod(7,9,11)
8
An assigned parameter can be used, even if it was not a parameter of the function. This simply defines a new local variable with this name.
>function f(x) := a*x^2
Since this is a one-line function, it defines "useglobal", and thus can read global variables.
>a:=4; f(2)
16
The local variable a=5 overrides the global variable.
>f(2,a=5)
20
Scalar parameters are passed to functions by value. If you change the value, it will not change the value of the variable passed to the function.
>function f(x) ...
x=4; endfunction
In the following example, a is not changed.
>a=5; f(a); a,
5
Of course, the name of the parameter inside the function does not matter at all. It is a local name.
>x=5; f(x); x,
5
To allow passing a parameter by reference, the parameter name must start with %... Even in this case, only the value of the parameter can be changed, not its size or type.
>function f(%x) ...
%x=4; endfunction
>x=5; f(x); x,
4
>x=I; f(x); x,
Cannot change type of non-local variable x! Error in : %x=4; ^ Error in function f
Vectors and matrices are passed by reference. So a function can change the elements of the vector or matrix.
>function f(x) ...
x[1]=5; endfunction
>x=1:10; f(x); x,
[ 5 2 3 4 5 6 7 8 9 10 ]
If you do not want this, you need to make a local copy. Even a copy to a variable with the same name will work. It is only possible to change elements of the vector or matrix, not the complete global matrix.
>function f(x) ...
x=x; x[1]=5; return x; endfunction
>x=1:10; f(x), x,
[ 5 2 3 4 5 6 7 8 9 10 ] [ 1 2 3 4 5 6 7 8 9 10 ]
If you want to change a matrix in the calling program or globally, you must return the matrix from the function and assign the returned value.
>function f(x) ...
y=x; y[1]=2; return y; endfunction
>x=f(x); x,
[ 2 2 3 4 5 6 7 8 9 10 ]
In one-line functions, the symbolic expression is evaluated at compile time.
>function f(x) &= diff(x^x,x)
x x (log(x) + 1)
The same is possible in multi-line functions. The syntax for this is &:"...".
>function f(x) ...
return x+&:"diff(x*exp(x),x)" endfunction
As you see, the function is indeed defined using the evaluated expression.
>type f
function f (x) return x+x*E^x+E^x endfunction
It is also possible to call Maxima at run time in functions. The syntax is &"...". Note that this will slow down your function considerably. However, with run time calls it is possible to include the value of a parameter into the Maxima command using the @... syntax.
>function drawtaylor (expr,a,b,n) ...
plot2d(expr,a,b); dexpr = &"taylor(@expr,x,0,@n)"; plot2d(dexpr,>add,color=blue,>add); labelbox([expr,dexpr],colors=[black,blue]); endfunction
>drawtaylor("exp(x)",-1,1,2):
A function can call itself. So we can also program recursively in Euler. Of course, we have to make sure the recursion is not infinite. In the following definition of the factorial function, we use a "return" inside an if clause to stop the recursion.
>function fact (n:natural) ...
if n<=0 then return 1; endif; return n*fact(n-1) endfunction
This defines the factorial function.
>fact(5)
120
We can achieve the same result with a loop. In the following definition, we use a simple integer loop. The loop index can be accessed with "#".
>function map fact1 (n:natural) ...
x=1; loop 2 to n; x=x*#; end; return x; endfunction
This should be a bit faster. Since we used the map keyword, it will also work for vector input.
>fact1(1:5)
[ 1 2 6 24 120 ]
The matrix language of Euler has a better solution for this.
>cumprod(1:5)
[ 1 2 6 24 120 ]
Let us try to evaluate the Chebyshev polynomial of degree n at x, using the recursive formula T(n,x)=2*x*T(n-1,x)-T(n-2,x). Of course, Euler has the function "cheb" already, but we want to demonstrate the program here. We could use a recursive definition, but is far more effective to make it in a loop.
>function t (n:natural, x:vector) ...
if n==0 then return ones(size(x)); endif; if n==1 then return x; endif; a=1; b=x; loop 2 to n; c=2*x*b-a; a=b; b=c; end; return b endfunction
The function works for vectors x, even if n=0. It does not work for vectors n.
>x=-1:0.01:1; plot2d("t(6,x)",title="t(6,x)",a=-1,b=1):
By the way, it is surprising how accurate the formula is. This is due to an error cancellation.
>longformat; t(60,0.9), cos(60*acos(0.9)),
-0.350468376614 -0.350468376614
Let us try to get the Chebyshev polynomial itself. We have to struggle with the vectors containing the coefficients of the polynomial.
>function T (n:natural) ...
if n<=0 then return [1]; endif; if n==1 then return [0,1]; endif; a=[1]; b=[0,1]; loop 2 to n; c=[0,2*b]-[a,0,0]; a=b; b=c; end; return b endfunction
Now T(n) returns the coefficients of the n-th Chebyshev polynomial.
>T(4)
[ 1 0 -8 0 8 ]
We can evaluate a polynomial with "evalpoly".
>plot2d("evalpoly(x,T(10))",title="T(10)",a=-1,b=1):
However, this is not an accurate procedure for large n. (Compare with the result above).
>p=T(60); evalpoly(0.9,p)
-14447.2139393
Though it is not efficient either, we can use an accurate solver for the evaluation of this polynomial. We see that the reason of the inaccuracy is the Horner scheme.
>xevalpoly(0.9,p)
-0.350468376613
Euler contains a very accurate evaluation of the Chebyshev polynomials.
>cheb(0.9,60)
-0.350468376614
Some functions require a vector input and output. But we might want to use variable names for the vector elements. Euler can use the following syntax for this.
>function f([x,y]) := [x*y-1,x-y]
The Broyden algorithm can solve systems of n equations with n unknowns. It expects a function f(v) mapping a vector to a vector. For an example, we find the solution of x*y-1=0 and x-y=0.
>broyden("f",[1,0])
[ 1 1 ]
The function f will work for column vectors, and for single elements, if these elements are scalar.
>f(1,1)
[ 0 0 ]
>f([1,1])
[ 0 0 ]
Many functions of Euler can use a function or an expression as parameters. To program such a function, we need to accept the function or expression in a string, and then evaluate it like a normal function or expression.
>function test (f:string,x) := f(x)
The string f can now be used for any other function, including built-in functions.
>test("sin",pi/2)
1
And it can also be used for expressions.
>test("x^2",2)
4
The reason for this is, that an expression expr using the variable x can be evaluated by expr(value). The evaluation of a string in the function test can use global variables.
>a:=5; test("x^2+a",2)
9
If a function or an expression passed by a string needs additional arguments, we can pass these arguments as semicolon parameters. Let us try a function first.
>function f(x,a) := a*x^2 >function test(f:string,x) := f(x,args())
Now we want to evaluate "f" with a=4 at the point 3.
>test("f",3;4)
36
What happens here is that the value 4 is passed as an additional argument to f by test(...). This is done by the args() parameter. The plot2d and other functions use the same trick to pass arguments to functions.
>plot2d("f",-1,1;1); plot2d("f";2,color=red,>add):
For expressions, this is only necessary for local variables, since expressions can use global variables.
>function g(x,a) ...
return test("a*x^2",x;a) endfunction
Without the ";a" the evaluation would use the global value of the variable a.
>g(2,5)
20
Note that the function args() can return multiple results, which will all be used as arguments.
>function f(x,a,b) := a*x+b*x^2
We can plot
now, setting a=-1 and b=2 with semicolon parameters for f.
>plot2d("f",-1,1;-1,2):
This technique is essential, if plot2d is used inside a function, and a local value needs to be passed to f. In most cases, using an expression is easier.
>plot2d("f(x,-1,2)",-1,1);
It is recommended to save functions in Euler files. These files have the extension ".e", and should be located in the directory of the notebook. The following is a test file I made for this example notebook. You can edit the file by pressing F9 or F10 in the next line. However, you might not be able to save your changes, since you cannot write to the installation directory.
>load test
This comment prints, when the file is loaded.
It loads the comment section of the Euler file. Here is the content of the file.
>printfile("test.e")
// * Test File comment This comment prints, when the file is loaded. endcomment // This is a paragraph of text for the HTML export // * Section I // Another paragraph of text. function test (x) ## Just a test function, prints x. return "You entered the parameter "+x; endfunction function testf (y) := y^y testvar := 5; // test variable
The functions in the file are now defined in Euler.
>test(23)
You entered the parameter 23
The help for test will work too, including the help in the status line.
>help test
test is an Euler function. function test (x) Function in file : test Just a test function, prints x. Search Maxima for help: Other Maxima functions. Try ":: ??item" for help. test_mean test_sign test_variance test_rank_sum test_normality test_proportion testsuite_files test_signed_rank test_variance_ratio test_means_difference test_proportions_difference
Also the variables defined in the file exist in Euler.
>testvar
5
You can export an Euler file to HTML with a menu entry in the File menu. To link to the HTML export use either of the following forms of links. test.e.html Euler file test.e The user can double click on the link. Euler files can call each other. Make sure, that no Euler file calls itself recursively.
Euler has a somewhat limited support for operators. There are infix, prefix, and postfix operators. The names of operators have the same restrictions as the names of functions. You cannot define symbols as operators. As a first example we define the operator pf for fractional print of a number.
>function prefix pf (x) := frac(x)
Then you can call the function pf without brackes.
>pf 1/6+1/7
13/42
As you see, the operatpr pf binds weakly. By internal reasons, it is not possible to define a strong prefix operator. But for infix and postfix operators, this is possible. The following operator will bind strong. Note that it overwrites the internal function mod. So we must call this internal function as _mod, and enable the overwrite flag.
>function overwrite strong operator mod (x:integer, n:index) := _mod(x,n)
Note that now the operator mod is evaluated before the + operator.
>11 mod 7 + 12 mod 7
9
It might be more practical to define a weak postfix operator for a specific prime.
>function postfix mod7 (x:integer) := _mod(x,7) >11+12 mod7
2