There are two slightly different syntactic forms provided for the definition of a user function (as opposed to an intrinsic function). For the case of a function whose definition can be expressed as a single expression, an abbreviated form is provided. The syntax for the definition of user procedures is similar. Names for functions and procedures are ordinary identifiers and so obey the rules as given in Chapter 1 for other variables.
This creates a function taking n≥0 arguments, and assigns it to f. The statements may comprise any number of valid Magma statements, but at least one of them must be of the form return expression;. The value of that expression (possibly dependent on the values of the arguments x1, ..., xn) will be the return value for the function; failure to return a value will lead to a run-time error when the function is invoked. (In fact, a return statement is also required for every additional `branch' of the function that has been created using an if ... then ... else ... construction.)The function may return multiple values. Usually one uses the form return expression, ..., expression;. If one wishes to make some return value(s) undefined (so that the number of return values for the function is the same in all `branches' of the function) the underscore symbol (_) may be used. (The undefined symbol may only be used for final values of the list.) This construct allows behaviour similar to the intrinsic function IsSquare, say, which returns true and the square root of its argument if that exists, and false and the undefined value otherwise. See also the example below.
If there are parameters given, they must consist of a comma-separated list of clauses each of the form identifier := value. The identifier gives the name of the parameter, which can then be treated as a normal value argument within the statements. The value gives a default value for the parameter, and may depend on any of the arguments or preceding parameters; if, when the function is called, the parameter is not assigned a value, this default value will be assigned to the parameter. Thus parameters are always initialized. If no parameters are desired, the colon following the last argument, together with parameters, may be omitted.
The only difference between the two forms of function declaration lies in recursion. Functions may invoke themselves recursively since their name is part of the syntax; if the first of the above declarations is used, the identifier f cannot be used inside the definition of f (and $$ will have to be used to refer to f itself instead), while the second form makes it possible to refer to f within its definition.
An invocation of the user function f takes the form f(m1, ..., mn), where m1, ..., mn are the actual arguments.
This is a short form of the function constructor designed for the situation in which the value of the function can be defined by a single expression. A function f is created which returns the value of the expression (possibly involving the function arguments x1, ..., xn). Optional parameters are permitted as above.
This creates a variadic function, which can take n or more arguments. The semantics are identical to the standard function definition described above, with the exception of function invocation. An invocation of a variadic function f takes the form f(y1, ..., ym), where y1, ..., ym are the arguments to the function, and m ≥n. These arguments get bound to the parameters as follows: for i < n, the argument yi is bound to the parameter xi. For i ≥n, the arguments yi are bound to the last parameter xn as a list [ * yn, ..., ym * ].
This is a short form of the function constructor for variadic functions, otherwise identical to the short form described above.
> fibonacci := function(n) > if n le 2 then > return 1; > else > return $$(n-1) + $$(n-2); > end if; > end function; > > fibonacci(10)+fibonacci(12); 199 > function Lucas(n) > if n eq 1 then > return 1; > elif n eq 2 then > return 3; > else > return Lucas(n-1)+Lucas(n-2); > end if; > end function; > > Lucas(11); 199 > fibo := func< n | n le 2 select 1 else $$(n-1) + $$(n-2) >; > fibo(10)+fibo(12); 199
> f := function(x, y: Proof := true, Al := "Simple") > return <x, y, Proof, Al>; > end function; > > f(1, 2); <1, 2, true, Simple> > f(1, 2: Proof := false); <1, 2, false, Simple> > f(1, 2: Al := "abc", Proof := false); <1, 2, false, abc>
> f := function(x) > if IsOdd(x) then > return true, x; > else > return false, _; > end if; > end function; > > f(1); true 1 > f(2); false > a, b := f(1); > b; 1 > a, b := f(2); > b; // Undefined value >> b; ^ User error: Identifier 'b' has not been assigned
> f := function(x, y, ...) > print "x: ", x; > print "y: ", y; > return [x + z : z in y]; > end function; > > f(1, 2); x: 1 y: [* 2*] [ 3 ] > f(1, 2, 3); x: 1 y: [* 2, 3*] [ 3, 4 ] > f(1, 2, 3, 4); x: 1 y: [* 2, 3, 4*] [ 3, 4, 5 ]
The procedure, taking n≥0 arguments and defined by the statements is created and assigned to p. Each of the arguments may be either a variable (yi) or a referenced variable (~yi). Inside the procedure only referenced variables (and local variables) may be (re-)assigned to. The procedure p is invoked by typing p(x1, ..., xn), where the same succession of variables and referenced variables is used (see the example below). Procedures cannot return values.If there are parameters given, they must consist of a comma-separated list of clauses each of the form identifier := value. The identifier gives the name of the parameter, which can then be treated as a normal value argument within the statements. The value gives a default value for the parameter, and may depend on any of the arguments or preceding parameters; if, when the function is called, the parameter is not assigned a value, this default value will be assigned to the parameter. Thus parameters are always initialized. If no parameters are desired, the colon following the last argument, together with parameters, may be omitted.
As in the case of function, the only difference between the two declarations lies in the fact that the second version allows recursive calls to the procedure within itself using the identifier (p in this case).
This is a short form of the procedure constructor designed for the situation in which the action of the procedure may be accomplished by a single statement. A procedure p is defined which calls the procedure given by the expression. This expression must be a simple procedure call (possibly involving the procedure arguments x1, ..., xn). Optional parameters are permitted as in the main procedure constructor.
Creates and assigns a new variadic procedure to p. The use of a variadic procedure is identical to that of a variadic function, described previously.
This is a short form of the procedure constructor for variadic procedures.
> procedure CheckPythagoras(x, y, z, ~h) > if x^2+y^2 eq z^2 then > h := true; > else > h := false; > end if; > end procedure;We use this to find some Pythagorean triples (in a particularly inefficient way):
> for x, y, z in { 1..15 } do > CheckPythagoras(x, y, z, ~h); > if h then > "Yes, Pythagorean triple!", x, y, z; > end if; > end for; Yes, Pythagorean triple! 3 4 5 Yes, Pythagorean triple! 4 3 5 Yes, Pythagorean triple! 5 12 13 Yes, Pythagorean triple! 6 8 10 Yes, Pythagorean triple! 8 6 10 Yes, Pythagorean triple! 9 12 15 Yes, Pythagorean triple! 12 5 13 Yes, Pythagorean triple! 12 9 15
The forward declaration of a function or procedure f; although the assignment of a value to f is deferred, f may be called from within another function or procedure already.The forward statement must occur on the `main' level, that is, outside other functions or procedures. (See also Chapter MAGMA SEMANTICS.)
First we define a simple function that proves primality of n by finding an integer of multiplicative order n - 1 modulo n.
> function strongTest(primdiv, n) > return exists{ x : x in [2..n-1] | \ > Modexp(x, n-1, n) eq 1 and > forall{ p : p in primdiv | Modexp(x, (n-1) div p, n) ne 1 } > }; > end function;Next we define a rather crude isPrime function: for odd n>3 it first checks for a few (3) random values of a that an - 1 ≡ 1 mod n, and if so, it applies the above primality prover. For that we need the not yet defined function for finding the prime divisors of an integer.
> forward primeDivisors; > function isPrime(n) > if n in { 2, 3 } or > IsOdd(n) and > forall{ a : a in { Random(2, n-2): i in [1..3] } | > Modexp(a, n-1, n) eq 1 } and > strongTest( primeDivisors(n-1), n ) > then > return true; > else > return false; > end if; > end function;Finally, we define a function that finds the prime divisors. Note that it calls the isPrime function. Note also that this function is recursive, and that it calls a function upon its definition, in the form func< ..> ( .. ).
> primeDivisors := function(n) > if isPrime(n) then > return { n }; > else > return func< d | primeDivisors(d) join primeDivisors(n div d) > > ( rep{ d : d in [2..Isqrt(n)] | n mod d eq 0 }); > end if; > end function; > isPrime(1087); true;