Application Center - Maplesoft

App Preview:

Maple Programming: 4.10: Procedure data structure

You can switch back to the summary page by clicking here.

Learn about Maple
Download Application


 

4.10.mws

Programming in Maple

Roger Kraft
Department of Mathematics, Computer Science, and Statistics
Purdue University Calumet

roger@calumet.purdue.edu

4.10. Procedure data structure

We have said that almost everything in Maple is a data structure. So it should not be a surprise that procedures are themselves another kind of data structure and that they have the data type procedure . Here is a simple (and somewhat silly) example.

>    f := proc(x::numeric,y)::numeric;

>      local u, v::int;

>      global w;

>      option trace;

>      description "a silly example";

>      u:=x; v:=y; w:=u+v+5;

>    end;

f := proc (x::numeric, y)::numeric; local u, v::int; global w; option trace; description

Let us check the data type of f .

>    whattype( f );

symbol

Oops, forgot about last name evaluation.

>    whattype( eval(f) );

procedure

Since our procedure is a data structure, we should be able to use the op  command to examine the operands of this data structure, that is, use op  to see what kind of data is stored in a procedure  data structure.

The op  command, however, works very strangely with procedure  data structures, so looking at the data in a procedure is a bit confusing. First, since Maple uses last name evaluation for procedures, the following command gives us the data type of the name f , not the data type of f 's value, the procedure.

>    op( 0, f );

symbol

The next command gives us the data type of the procedure which is the value of f .

>    op( 0, eval(f) );

procedure

But in the next two commands, op  switches to full evaluation and shows us the definition of the procedure named by f .

>    op( f );

proc (x::numeric, y)::numeric; local u, v::int; global w; option trace; description

>    op( 1, f );

proc (x::numeric, y)::numeric; local u, v::int; global w; option trace; description

But the definition of the procedure is not the data stored in the procedure  data structure. To get at the data stored in the procedure  data structure, we use the following command.

>    op( eval(f) );

x::numeric, y, u, v::int, trace,

There are actually eight operands in a procedure  data structure, as the next command shows.

>    nops( eval(f) );

8

 The next sequence of commands shows what is in each of the eight operands. A couple of these data items are currently empty in the data structure for f .

>    op( 1, eval(f) );

x::numeric, y

>    op( 2, eval(f) );

u, v::int

>    op( 3, eval(f) );

trace

>    op( 4, eval(f) );

>    op( 5, eval(f) );

>    op( 6, eval(f) );

w

>    op( 7, eval(f) );

>    op( 8, eval(f) );

numeric

Notice that the first operand of a procedure  data structure is an expression sequence of formal parameters. If a formal parameter has a type declaration, then the type declaration is held in a `::`  data structure.

>    op( 1, [op( 1, eval(f) )] );

x::numeric

>    whattype( op( 1, [op(1, eval(f))] ) );

`::`

The second operand of a procedure  data structure is an expression sequence of local variables. If a local variable has a type assertion, then the type assertion is held in a `::`  data structure (we will not make use of type assertions in these worksheets).

>    op( 2, [op( 2, eval(f) )] );

v::int

>    whattype( op( 2, [op(2, eval(f))] ) );

`::`

Notice that the definition of the procedure is nowhere in the procedure  data structure.

>   

To summarize, op(f)  returns the definition of the procedure and op(eval(f))  returns the contents of the procedure  data structure.

>    op( f );

proc (x::numeric, y)::numeric; local u, v::int; global w; option trace; description

>    op( eval(f) );

x::numeric, y, u, v::int, trace,

There are eight operands in a procedure  data structure and we access them using op(i, eval(f))  with i   from 1 to 8. The first operand is the list of formal parameter names. The second operand is the list of local variable names. The third operand is a list of options. The fourth operand is called the remember table (we will discuss this operand in the next section). The fifth operand is a descriptive string. The sixth operand is the list of global variable names. The seventh operand is called the lexical table. Finally, the eighth operand is the (optional) return type.

Exercise : Explain what the following procedure does and how it does it. In particular, explain the reasons for the right-quotes.

>    pds := proc(p::procedure)

>      local i;

>      for i from 1 to 8 do

>        print('op'(i, 'eval'(p))=op(i, eval(p)))

>      od;

>    end;

pds := proc (p::procedure) local i; for i to 8 do print(('op')(i,('eval')(p)) = op(i,eval(p))) end do end proc

Modify the procedure pds  so that it displays a more descriptive message on the left hand side of each equal sign.

>   

Of the eight operands in a procedure data structure, we already know what three of them represent. They are the formal parameters, the local variables, and the global variables. Of the remaining five, the remember table is the most important and we will discuss it in the next section. The descriptive string seems pretty self explanatory. The lexical table lists what are called "lexically scoped variables". These are variables that can only occur when one procedure is defined inside the body of another procedure. We will say more about this in a later (optional) section. The options operand we discuss next. And we will not be making any use of the "return type" in these worksheets.

A procedure definition can contain an optional option  section. There are eight different properties that can be declared in the option  section, arrow , builtin , inline , operator , remember , system , trace  and Copyright . Right now we want to describe just three of these, the operator , arrow  and trace  options. The remember  option will be described in the next section. The builtin  and Copyright  options will be discussed in the worksheet on Maple programming. The other options we will not mention any further.

If a procedure is defined with both the operator  and arrow  options, then the procedure acts exactly as if it were defined using the arrow notation.

>    g := proc(x)

>      option operator, arrow;

>      x^2+2*x-1

>    end;

g := proc (x) options operator, arrow; x^2+2*x-1 end proc

>    eval( g );

proc (x) options operator, arrow; x^2+2*x-1 end proc

Notice that if we define a function using the arrow notation

>    h := x -> x^2-2*x+1;

h := proc (x) options operator, arrow; x^2-2*x+1 end proc

and then examine the option operand of the function's procedure  data structure

>    op( 3, eval(h) );

operator, arrow

it will have both the operator  and arrow  options. A procedure can have one of the operator  or arrow  options without the other, but we will not go into what that means.

Exercise : Try defining a procedure with just the operator  option or just the arrow  option. Do they act like functions defined using the arrow notation?

>   

Now we shall mention the trace  option. This is an option that is used for debugging a procedure. When we write procedures that are a bit complicated, it is very possible that we can make a mistake in the definition of the procedure and the procedure does not do what we expect it to do. Finding mistakes can be difficult, and the trace  option is meant to help.

When a procedure has the trace  option, Maple will print out quite a bit of information about the procedure whenever it is called. Here is an example with our silly function f , which has the trace  option.

>    f( 1, 2 );

{--> enter f, args = 1, 2

u := 1

v := 2

w := 8

<-- exit f (now at top level) = 8}

8

Let us define another procedure with the trace  option that calls the procedure f .

>    g := proc(x,y)

>      local u,v;

>      option trace;

>      u := f(x,x);

>      v := f(y,y);

>      u + v;

>    end;

g := proc (x, y) local u, v; option trace; u := f(x,x); v := f(y,y); u+v end proc

Now let us call g  to see what Maple produces for us.

>    g( 2, 3 );

{--> enter g, args = 2, 3

{--> enter f, args = 2, 2

u := 2

v := 2

w := 9

<-- exit f (now in g) = 9}

u := 9

{--> enter f, args = 3, 3

u := 3

v := 3

w := 11

<-- exit f (now in g) = 11}

v := 11

20

<-- exit g (now at top level) = 20}

20

What Maple is doing is giving us a "trace", or a history, of everything that goes on inside of the procedures g  and f . Hopefully this information will be of use when we are trying to find out what we did wrong in the definition of a procedure. Here is another example. The following procedure takes as input a name and a positive integer and it truncates the name to the integer number of letters.

>    shorten := proc(x::name, n::posint)

>      local i, y;

>      option trace;

>      y := convert( x, string );

>      y := seq( y[i], i=1..n );

>      y := cat( y );

>      convert( y, name );

>    end;

shorten := proc (x::name, n::posint) local i, y; option trace; y := convert(x,string); y := seq(y[i],i = 1 .. n); y := cat(y); convert(y,name) end proc

Let us call this procedure.

>    shorten( `a name that is too long`, 6 );

{--> enter shorten, args = a name that is too long, 6

y :=

y :=

y :=

`a name`

<-- exit shorten (now at top level) = a name}

`a name`

Notice how we can see all of the steps that occur inside the procedure as they are executed.

>   

Exercise : What do you think should happen if you call shorten  with the number n  larger than the length of the name x ? What does happen and why? Notice how the trace lets you see what is going on inside of a procedure so that you can more easily answer questions like these.

>   

Exercise : The following procedure was meant to reverse the letters in a name, so reverse_name(hello)  was supposed to output olleh . But there is a bug in the procedure. Give the procedure the trace option  and find the bug.

>    reverse_name := proc(x::name)

>      local i, y;

>      y := convert( x, string );

>      y := seq( x[-i], i=1..length(y) );

>      y := cat( y );

>      convert( y, name );

>    end;

reverse_name := proc (x::name) local i, y; y := convert(x,string); y := seq(x[-i],i = 1 .. length(y)); y := cat(y); convert(y,name) end proc

>    reverse_name('hello');

`hello[-1] || (hello[-2]) || (hello[-3]) || (hello[-4]) || (hello[-5])`

>   

Exercise : Explain the relationship between a procedure  data structure and function  data structure. The following two help pages might help a bit.

>    ?type,procedure

>    ?type,function

>   

>   

>