Lexical Scoping
Maple allows you to create nested procedure definitions. This means that procedures can be defined within other procedures or can return procedures as expected output. Lexical scoping allows a nested procedure to access the variables that are located in surrounding procedures. You can now program in Maple and achieve better encapsulation.
Because Maple is symbolic and can manipulate objects such as procedures and unevaluated local variables from procedures, lexical scoping also provides a mechanism to do object-oriented programming.
Scoping Rules
When a variable is encountered in a proc, the following rules are applied to determine its binding:
1. If it is declared local within that procedure, it is local to that procedure.
2. If it is declared global within that procedure, it is the specified global variable.
3. If it is a parameter of that procedure, then that is what it is.
4. If it is not declared, it is searched for among the parameters and variables (implicitly or explicitly declared locals and explicitly declared globals) of the surrounding procedure or procedures, starting with the innermost. If the name is encountered as a parameter, encountered in a local or in a global statement, or is implicitly declared local by being assigned to or used as the control variable of a for loop, that is where the variable is bound.
5. If it is not declared and not found in the parameters or variables of the surrounding procedures, the existing rules for implicit declarations are used: If the name appears on the left-hand side of an assignment or as the controlling variable of a for loop, it is local; otherwise, it is global.
More simply, this can be written as two rules:
1. The name is searched for among the parameters; among the local and global declarations of the procedure; and then among the parameters, the local and global declarations, and the implicitly declared local variables of any surrounding procedures, from the inside out. If the name is found, that specifies the binding of the variable.
2. If the name is not declared within the procedure or the surrounding procedures, the following rule is used: If the name appears within the procedure on the left-hand side of an assignment or as the controlling variable of a for loop, it is local to that procedure; otherwise, it is global.
Note: Lexical scoping occurs before the implicit declaration rules. In other words, an identifier will not be implicitly declared local until it has been determined that it has not been declared (as implicitly local or as explicitly local or global) in an outer procedure.
Illustration of Rules
restart;
The following example is used to illustrate these rules.
P:=proc( p1 ) local l1; global g1; il2 := l1 + g1; proc( p2 ) local l3; global g2; l1 := l3 + g2; il2 := g2 + l3; l4 := l1 + il2 + g4; proc( p3 ) l1 + il2 + l3 + g1 + g2 + g3 + g4; end proc; end proc; end proc:
Warning, (in P) `il2` is implicitly declared local
Warning, (in anonymous procedure within P) `l4` is implicitly declared local
The outermost procedure has one parameter (p1), one explicit local (l1), one implicit local (il2), and one global (g1).
The second procedure has one parameter (p2), one explicit local (l3), one implicit local (il2), and one global (g2). Notice that the il2 in the second procedure is the same as the il2 in the outermost procedure, and that the l1 in the second procedure is the same as the l1 in the outer procedure.
In the innermost procedure, l1 and il2 are the local ones from the outermost procedure and l3 is the local one from the second procedure. The four variables g1, g2, g3, and g4 are all global. Variable g1 is global because it is declared as such in the outermost procedure. Variable g2 is global because it is declared as such in the second procedure. Variables g3 and g4 are global by the default rules.
Local Procedures
With the advent of lexical scoping, it is now possible for an outer procedure to return an inner procedure that continues to access local variables of the outer procedure. For example:
makecounter := proc( max::integer ) local counter; counter := 0; proc() counter := `if`( counter >= max, 1, counter + 1 ); end proc; end proc:
c1 := makecounter(3); c2 := makecounter(5);
c1 ≔ proccounter ≔ if⁡3<=counter,1,counter+1end proc
c2 ≔ proccounter ≔ if⁡5<=counter,1,counter+1end proc
In the above, c1 and c2 are both counters, one of which will count from 1 to 3 repeatedly, and the other from 1 to 5, incrementing each time they are called. Each counter refers to a private copy of max, and each is incrementing a private copy of counter. But because counter is not local to the returned procedure, it is not destroyed each time the returned procedure exits, and thus it retains its states between invocations. In other words, it is a private static variable to the returned inner procedure once makecounter has returned.
These facilities allow one to do object-oriented programming in a very clean way in Maple. A function like makecounter is a constructor, and the returned procedure represents the object and a single method. By returning a more complex object procedure, multiple methods can be simulated (for example, the procedure can be called with an index).
Return to Index for Example Worksheets
Download Help Document