[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2 Writing user defined functions


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.1 User defined functions

To define functions by an user himself, ‘def’ statement must be used. Syntactical errors are detected in the parsing phase of Asir, and notified with an indication of where Asir found the error. If a function with the same name is already defined (regardless to its arity,) the new definition will override the old one, and the user will be told by a message,

afo() redefined.

on the screen when a flag verbose is set to a non-zero value by ctrl(). Recursive definition, and of course, recursive use of functions are available. A call for an yet undefined function in a function definition is not detected as an error. An error will be detected at execution of the call of that yet undefined function.

def f(X) { 
    if ( !X )
        return 1;
    else 
        return X * f(X-1);
}


def c(N)
{
    A = newvect(N+1); A[0] = B = newvect(1); B[0] = 1;
    for ( K = 1; K <= N; K++ ) {
        A[K] = B = newvect(K+1); B[0] = B[K] = 1;
        for ( P = A[K-1], J = 1; J < K; J++ ) 
            B[J] = P[J-1]+P[J];
        }
    return A;
}


def add(A,B)
"add two numbers."
{
    return A+B;
}

In the second example, c(N) returns a vector, say A, of length N+1. A[I] is a vector of length I+1, and each element is again a vector which contains ICJ as its elements.

References

help.

In the following, the manner of writing Asir programs is exhibited for those who have no experience in writing C programs.


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.2 variables and indeterminates

variables (program variables)

A program variable is a string that begins with a capital alphabetical letter followed by any numbers of alphabetical letters, digits and ‘_’.

A program variable is thought of a box (a carrier) which can contain Asir objects of various types. The content is called the ‘value’ of that variable. When an expression in a program is to be evaluated, the variable appearing in the expression is first replaced by its value and then the expression is evaluated to some value and stored in the memory. Thus, no program variable appears in objects in the internal form. All the program variables are initialized to the value 0.

[0] X^2+X+1;
1
[1] X=2;
2
[2] X^2+X+1;
7
indeterminates

An indeterminate is a string that begins with a small alphabetical letter followed by any numbers of alphabetical letters, digits and ‘_’.

An indeterminate is a transcendental element, so-called variable, which is used to construct polynomial rings. An indeterminate cannot have any value. No assignment is allowed to it.

[3] X=x;
x
[4] X^2+X+1;
x^2+x+1
[5] A='Dx'*(x-1)+x*y-y;
(y+Dx)*x-y-Dx
[6] function foo(x,y);
[7] B=foo(x,y)*x^2-1;
foo(x,y)*x^2-1

[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.3 parameters and arguments

def sum(N) {
    for ( I = 1, S = 0; I <= N; I++ )
        S += I;
    return S;
}

This is an example definition of a function that sums up integers from 1 to N. The N in sum(N) is called the (formal) parameter of sum(N). The example shows a function of the single argument. In general, any number of parameters can be specified by separating by commas (‘,’). A (formal) parameter accepts a value given as an argument (or an actual parameter) at a function call of the function. Since the value of the argument is given to the formal parameter, any modification to the parameter does not usually affect the argument (or actual parameter). However, there are a few exceptions: vector arguments and matrix arguments.

Let A be a program variable and assigned to a vector value [ a, b ]. If A is given as an actual parameter to a formal parameter, say V, of a function, then an assignment in the function to the vector element designator V[1], say V[1]=c;, causes modification of the actual parameter A resulting A to have an altered value [ a c ]. Thus, if a vector is given to a formal parameter of a function, then its element (and subsequently the vector itself) in the calling side is modified through modification of the formal parameter by a vector element designator in the called function. The same applies to a matrix argument. Note that, even in such case where a vector (or a matrix) is given to a formal parameter, the assignment to the whole parameter itself has only a local effect within the function.

def clear_vector(M) {
    /* M is expected to be a vector */
    L = size(M)[0];
    for ( I = 0; I < L; I++ )
        M[I] = 0;
}

This function will clear off the vector given as its argument to the formal parameter M and return a 0 vector.

Passing a vector as an argument to a function enables returning multiple results by packing each result in a vector element. Another alternative to return multiple results is to use a list. Which to use depends on cases.


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.4 comments

The text enclosed by ‘/*’ and ‘*/’ (containing ‘/*’ and ‘*/’) is treated as a comment and has no effect to the program execution as in C programs.

/*
 * This is a comment.
 */

def afo(X) {

A comment can span to several lines, but it cannot be nested. Only the first ‘/*’ is effective no matter how many ‘/*’’s in the subsequent text exist, and the comment terminates at the first ‘*/’.

In order to comment out a program part that may contain comments in it, use the pair, #if 0 and #endif. (See section preprocessor.)

#if 0
def bfo(X) {
/* empty */
}
#endif

[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.5 statements

An user function of Asir is defined in the following form.

def name(parameter, parameter,...,parameter) {
    statement
    statement
    ...
    statement
}    

As you can see, the statement is a fundamental element of the function. Therefore, in order to write a program, you have to learn what the statement is. The simplest statement is the simple statement. One example is an expression with a terminator (‘;’ or ‘$’.)

S = sum(N);

A ‘return statement’ and ‘break statement’ are also primitives to construct ‘statements.’ As you can see the syntactic definition of ‘if statement’ and ‘for statement’, each of their bodies consists of a single ‘statement.’ Usually, you need several statements in such a body. To solve this contradictory requirement, you may use the ‘compound statement.’ A ‘compound statement’ is a sequence of ‘statement’s enclosed by a left brace ‘{’ and a right brace ‘}’. Thus, you can use multiple statement as if it were a single statement.

if ( I == 0 ) {
    J = 1;
    K = 2;
    L = 3;
}

No terminator symbol is necessary after ‘}’, because ‘{’ statement sequence ‘}’ already forms a statement, and it satisfies the syntactical requirement of the ‘if statement.’


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.6 return statement

There are two forms of return statement.

return expression;

return;

Both forms are used for exiting from a function. The former returns the value of the expression as a function value. The function value of the latter is not defined.


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.7 if statement

There are two forms of if statement.

if ( expression )             if ( expression )
     statement       and           statement
else  
     statement

The interpretation of these forms are obvious. However, be careful when another if statement comes at the place for ‘statement’. Let us examine the following example.

if ( expression1 )
    if ( expression2 ) statement1
else  
    statement2

One might guess statement2 after else corresponds with the first if ( expression1 ) by its appearance of indentation. But, as a matter of fact, the Asir parser decides that it correspond with the second if ( expression2 ). Ambiguity due to such two kinds of forms of if statement is thus solved by introducing a rule that a statement preceded by an else matches to the nearest preceding if.

Therefore, rearrangement of the above example for improving readability according to the actual interpretation gives the following.

if ( expression1 ) {
    if ( expression2 ) statement1 else statement2
}    

On the other hand, in order to reflect the indentation, it must be written as the following.

if ( expression1 ) {
    if ( expression2 ) statement1
} else
    statement2

When if is used in the top level, the if expression should be terminated with $ or ;. If there is no terminator, the next expression will be skipped to be evaluated.


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.8 loop, break, return, continue

There are three kinds of statements for loops (repetitions): the while statement, the for statement, and the do statement.

As means for exiting from loops, there are break statement and return statement. The continue statement allows to move the control to a certain point of the loop.


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.9 structure definition

A structure data type is a fixed length array and each component of the array is accessed by its name. Each type of structure is distinguished by its name. A structure data type is declared by struct statement. A structure object is generated by a builtin function newstruct. Each member of a structure is accessed by an operatator ->. If a member of a structure is again a structure, then the specification by -> can be nested.

[1] struct rat {num,denom};
0
[2] A = newstruct(rat);
{0,0}
[3] A->num = 1;
1
[4] A->den = 2;
2
[5] A;
{1,2}
[6] struct_type(A);
1
References

newstruct, struct_type


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.10 various expressions

Major elements to construct expressions are the following:


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.11 preprocessor

he Asir user language imitates C language. A typical features of C language include macro expansion and file inclusion by the preprocessor cpp. Also, Asir read in user program files through cpp. This enables Asir user to use #include, #define, #if etc. in his programs.

the following are the macro definitions in ‘defs.h’.

#define ZERO 0
#define NUM 1
#define POLY 2
#define RAT 3
#define LIST 4 
#define VECT 5
#define MAT 6
#define STR 7
#define N_Q 0
#define N_R 1
#define N_A 2
#define N_B 3
#define N_C 4
#define V_IND 0
#define V_UC 1
#define V_PF 2
#define V_SR 3
#define isnum(a) (type(a)==NUM)
#define ispoly(a) (type(a)==POLY)
#define israt(a) (type(a)==RAT)
#define islist(a) (type(a)==LIST)
#define isvect(a) (type(a)==VECT)
#define ismat(a) (type(a)==MAT)
#define isstr(a) (type(a)==STR)
#define FIRST(L) (car(L))
#define SECOND(L) (car(cdr(L)))
#define THIRD(L) (car(cdr(cdr(L))))
#define FOURTH(L) (car(cdr(cdr(cdr(L)))))
#define DEG(a) deg(a,var(a))
#define LCOEF(a) coef(a,deg(a,var(a)))
#define LTERM(a) coef(a,deg(a,var(a)))*var(a)^deg(a,var(a))
#define TT(a) car(car(a))
#define TS(a) car(cdr(car(a)))
#define MAX(a,b) ((a)>(b)?(a):(b))

Since we are utilizing the C preprocessor, it cannot properly preprocess expressions with $. For example, even if LIST is defined, LIST in the expression LIST$ is not replaced. Add a blank before $, i.e., write as LIST $ to make the proprocessor replace it properly.


[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.12 option

If a user defined function is declared with N arguments, then the function is callable with N arguments only.

[0] def factor(A) { return fctr(A); }
[1] factor(x^5-1,3);
evalf : argument mismatch in factor()
return to toplevel

A function with indefinite number of arguments can be realized by using a list or an array as its argument. Another method is available as follows:

% cat factor
def factor(F)
{
    Mod = getopt(mod);
    ModType = type(Mod);
    if ( ModType == 1 ) /* 'mod' is not specified. */   
        return fctr(F);
    else if ( ModType == 0 ) /* 'mod' is a number */
        return modfctr(F,Mod);
}  
[0] load("factor")$
[1] factor(x^5-1);
[[1,1],[x-1,1],[x^4+x^3+x^2+x+1,1]]
[2] factor(x^5-1|mod=11);
[[1,1],[x+6,1],[x+2,1],[x+10,1],[x+7,1],[x+8,1]]

In the second call of factor(), |mod=11 is placed after the argument x^5-1, which appears in the declaration of factor(). This means that the value 11 is assigned to the keyword mod when the function is executed. The value can be retrieved by getopt(mod). We call such machinery option. If the option for mod is not specified, getopt(mod) returns an object whose type is -1. By this feature, one can describe the behaviour of the function when the option is not specified by if statements. After ‘|’ one can append any number of options seperated by ‘,’.

[100] xxx(1,2,x^2-1,[1,2,3]|proc=1,index=5);

Optinal arguments may be given as a list with the key word option_list as option_list=[["key1",value1],["key2",value2],...]. It is equivalent to pass the optional arguments as key1=value1,key2=value2,....

[101] dp_gr_main([x^2+y^2-1,x*y-1]|option_list=[["v",[x,y]],["order",[[x,5,y,1]]]]); 

Since getopt() returns an option list, the optional argument option_list=... is useful when we call functions with optional arguments from a function with optional arguments to pass the all optional parameters.

% cat foo.rr
def foo(F)
{
    OPTS=getopt();
    return factor(F|option_list=OPTS);
}
[3] load("foo.rr")$
[4] foo(x^5-1|mod=11);
[[1,1],[x+6,1],[x+2,1],[x+10,1],[x+7,1],[x+8,1]]

[ << ] [ < ] [ Up ] [ > ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.13 module

Function names and variables in a library may be encapsulated by module. Let us see an example of using module

module stack;

static Sp $
Sp = 0$
static Ssize$
Ssize = 100$
static Stack $
Stack = newvect(Ssize)$
localf push $
localf pop $

def push(A) {
  if (Sp >= Ssize) {print("Warning: Stack overflow\nDiscard the top"); pop();}
  Stack[Sp] = A;
  Sp++;
}
def pop() {
  local A;
  if (Sp <= 0) {print("Stack underflow"); return 0;}
  Sp--;
  A = Stack[Sp];
  return A;
}
endmodule;

def demo() {
  stack.push(1);
  stack.push(2);
  print(stack.pop());
  print(stack.pop());
}

Module is encapsulated by the sentences module module name and endmodule. A variable of a module is declared with the key word static. The static variables cannot be refered nor changed out of the module, but it can be refered and changed in any functions in the module. The static variables must be declared before the definitions of functions, because the one-path parser of asir automatically assume variables as local variables if there is no declaration for them. A global variable which can be refered and changed in or out of the module is declared with the key word extern.

Any function defined in a module must be declared forward with the keyword localf. In the example above, push and pop are declared. This declaration is necessary.

A function functionName defined in a module moduleName can be called by the expression moduleName.functioName(arg1, arg2, ...) out of the module. Inside the module, moduleName. is not necessary. In the example below, the functions push and pop defined in the module stack are called out of the module.

 stack.push(2);
 print( stack.pop() );
 2

Any function name defined in a module is local. In other words, the same function name may be used out of the module to define a different function.

The module structure of asir is introduced to develop large libraries. In order to load libraries on demand, the command module_definedp will be useful. The below is an example of demand loading.

if (!module_definedp("stack")) load("stack.rr") $

It is not necessary to declare local variables in asir. As you see in the example of the stack module, we may declare local variables by the key word local. Once this key word is used, asir requires to declare all the variables. In order to avoid some troubles to develop a large libraries, it is recommended to use local declarations.

When we need to call a function in a module before the module is defined, we must make a prototype declaration as the example below.

/* Prototype declaration of the module stack */
module stack;
localf push $
localf pop $
endmodule;  

def demo() {
  print("----------------");
  stack.push(1);
  print(stack.pop());
  print("---------------");
}

module stack;
  /* The body of the module stack */
endmodule;

In order to call functions defined in the top level from the inside of a module, we use :: as in the example below.

def afo() {
  S = "afo, afo";
  return S;
}
module abc;
localf foo,afo $

def foo() {
  G = ::afo();
  return G;
}
def afo() {
  return "afo, afo in abc";
}
endmodule;
end$

[1200] abc.foo();
afo, afo
[1201] abc.afo();
afo, afo in abc
References

module_list, module_definedp, remove_module.


[ << ] [ < ] [ Up ] [ > ] [ >> ]

This document was generated on March 29, 2024 using texi2html 5.0.