------------------------------------------------------------------------- PHYS210 MAPLE PROGRAMMING LAB 2 ------------------------------------------------------------------------- ------------------------------------------------------------------------- SAMPLE SOLUTIONS TO EXERCISE 3 PROM PREVIOUS LAB ------------------------------------------------------------------------- o Can be found in ~phys210/maplesolns/myprocs2soln % cd ~phys210/maplesolns % ls index.html max3 myifCbad myprocs2soln tmax3 tmyifCbad o Look at myprocs2soln % more myprocs2soln o Also look at myifCbad % more myifCbad o ... will return to this example (procedure that returns largest of maximum of three values) later in lab ------------------------------------------------------------------------- TESTING PROCEDURES ------------------------------------------------------------------------- o Whenever you create a file that contains definitions of maple procedures, it is an excellent idea (i.e. recommended practice)! to create another file which tests the procedures, both with valid and invalid input. o Example: Open a terminal and change to the 'maplepgm' directory we created/used in the last lab, and get a directory listing % cd % cd maplepgm o Examine the file 'tprocs' using 'more', or point your browser to /home/phys210/maplepgm/index.html and click on the 'tprocs' link o Start xmaple from the same terminal, and read in 'tprocs' % xmaple & > read tprocs; o You should see output like the following "Testing of myadd begins" 5 w + z 2 2 sin(x) + cos(x) "a" + "b" "Testing of myadd ends" "Testing of myadd with more info begins" "The value of myadd(2, 3) is", 5 "The value of myadd(w, z) is", w + z 2 2 "The value of myadd(sin(x)^2, cos(x)^2) is", sin(x) + cos(x) "The value of myadd("a"+"b") is", "a" + "b" "Testing of myadd with more info ends" o Preparing a testing file for each file of procedures will ultimately save time when testing procedures, versus doing the testing interactively ------------------------------------------------------------------------- IMPORTANT --- CHANGE OF SETUP ------------------------------------------------------------------------- o Exit the xmaple session (don't save the worksheet), then create a new subdirectory in your home directory called maplepgm2. cd to it and copy all the files from ~phys210/maplepgm2 % cd % mkdir maplepgm2 % cd maplepgm2 % cp ~phys210/maplepgm2/* . o Get a listing of the directory, and you should see the following % ls errif glob glob1 imp index.html loc loc1 procs2 tprocs2 o You can look at these files using 'more' in a terminal, or like last lab you can point your browser to /home/phys210/maplepgm2/index.html and then click on the appropriate links o Start xmaple from the command line (in ~/maplepgm2) % xmaple & ------------------------------------------------------------------------- GLOBAL AND LOCAL VARIABLES ------------------------------------------------------------------------- o We have seen the use of VARIABLES in maple, which (as in most programming languages), are used as locations in which to store values that will generally change as a procedure/program executes. o Example: myfor := proc(n::integer) discussed in class myfor1 := proc(n::integer) for i from 1 to n do print(i); end do; end proc; o Here 'i' is a variable o There are two types of variables in maple that will be of interest to us. ------------------------------------------------------------------------- GLOBAL VARIABLES ------------------------------------------------------------------------- o For our purposes, every maple variable is given by a maple name. o A GLOBAL VARIABLE is a variable such that when the name is used outside all procedures (e.g. at the maple prompt), or inside ANY procedure (with an exception that we will see shortly), the same value will be returned. (**) o Again, this concept is best illustrated by example, so let's first look at the file 'glob', either with a browser, or with 'more' o Now read 'glob', and note the output > read glob; globalvar := 100 "The value of globalvar outside, and before the procedure call is", 100 "The value of globarvar inside the procedure is", 100 "The value of globalvar outside, and after the procedure call is", 100 o Note how, as the output indicates, the value of 'globalvar' inside and outside of the procedure is the same o Modify the value of 'globalvar', and invoke 'myglobal()' again: BE SURE TO TYPE THE () (empty argument list), or 'myglobal' will NOT be executed > globalvar := 200; > myglobal(); "The value of globarvar inside the procedure is", 200 o ... so the change of the value of 'globalvar' outside of the procedure affects the value inside. ------------------------------------------------------------------------- LOCAL VARIABLES ------------------------------------------------------------------------- o In contrast to a global variable, a LOCAL VARIABLE is one which is defined WITHIN A PROCEDURE and whose value is LOCAL to the procedure. If different procedures define a variable with the same name to be local, then the value of the variable in one procedure will not affect the value of that name in another procedure, or the value outside all procedures (i.e. at the maple command line). o IMPORTANT!! ---------------------------------------------------------------- o Within a procedure, we explicitly define variables to be local using the 'local' statement. ---------------------------------------------------------------- o Example: Examine the file 'loc', note the use of the 'local' statements o Read 'loc', and note the output > read loc; localvar := 10 "The value of localvar outside, and before the procedure calls is", 10 "The value of localvar inside 'mylocal1' is", 3.141592654 "The value of localvar inside 'mylocal2' is", 2.718281828 "The value of localvar outside, and after the procedure calls is", 10 ... so we see that the values of 'localvar' at the maple command prompt, and within the two procedures do not "conflict" with one another o Verify this: first set 'localvar' at the interactive prompt to a different value > localvar := 60; the execute mylocal1() and mylocal2() again > mylocal1(); mylocal2(); o Note again that when a vbl is declared local to a procedure, it "overrides" any value of a vbl with the same name defined at the command line (i.e. this is the exception to the rule (**) above) ------------------------------------------------------------------------- IMPLICIT DECLARATION OF GLOBAL AND LOCAL VARIABLES ------------------------------------------------------------------------- o If a maple name has been assigned a value BEFORE a procedure definition is made, and that procedure uses the name as a variable, then the variable is IMPLICITLY global o If a maple name has NOT been assigned a value before a procedure definition is made, and that procedure uses the name as a variable, without an explicit 'local' declaration, then the variable is implicitly declared to be local, and a warning message is issued o Example: Examine the contents of the file 'loc1' and then read it in > read loc1; Warning, `mylocal3var` is implicitly declared local to procedure `mylocal3` . . . > mylocal3(); "The value of mylocal3var inside 'mylocal3' is", 3.141592654 o You should ALWAYS declare local variables to be such ... I will do so in all of the examples that follow, and you should as well in the lab exercises and especially in the homework. o SO, if you see one of more warnings such as the above, we sure to modify your procedure definition to explicitly declare all local variables o NOTE: If a procedure uses more than one local vbl, they can be defined either with a single statement, or multiple statements, e.g. local var1, var2, var3; and local var1; local var2; local var3; are equivalent ----------------------------------------------------------------------- VERY IMPORTANT! ----------------------------------------------------------------------- o 'local' statements must be placed IMMEDIATELY AFTER THE 'proc' STATEMENT (the procedure header). They cannot come after any assignment statements, if statements, for statements etc. ------------------------------------------------------------------------- IMPORTANT: CHANGE OF SETUP ------------------------------------------------------------------------- o Input file 'procs2' that contains procedure definitions for remainder of lab > read procs2; ------------------------------------------------------------------------- FOR LOOPS ------------------------------------------------------------------------- o Saw basic form in lecture o -> numeric expression (best to make it integer) o -> Loop variable (changes at every iteration of o -> Statement sequence (body of loop) o Loop body executed repeatedly, with loop variable varying o at each iteration o Again, best to keep to integer values for from to do end do; o More general form for from by to do end do; o -> 'by' specifies loop increment (step) ------------------------------------------------------------------------- SAMPLE PROCEDURES USING FOR STATEMENTS ------------------------------------------------------------------------- o Note: These 3 examples use the 'print' command so that the operation of the loop is clearer, but remember that you can *not* use 'print' to return a value from a procedure. Also observe the declaration of the loop variable 'i' to be 'local' in all 3 cases o Note: As we work through the examples, it is best to also look at the full definitions in the file 'procs2', so that the structures of the procedures are clearer, and so that you can read the comments -------------------------------- o myfor1: Most basic form of for -------------------------------- > op(myfor1); proc(n::integer) local i; for i to n do print(i) end do end proc > myfor1(5); 1 2 3 4 5 o BEGIN ASIDE o IMPORTANT: Can't stress this enough ... In these examples, we use 'print' statements to explicitly illustrate the execution of the loops. NONE of the values that are displayed by these statements are returned by the procedures so, again, when coding your own procedures that use loop DO NOT ATTEMPT to use 'print' to return a value. o In fact, every 'print' statement returns a special value, NULL, (all upper case, and a Maple reserved word), which essentially means "nothing". Try the following > NULL; ... and after you press Enter, you should see that Maple replies with literally nothing > res := print("hello"); "hello" res := o Explicitly demonstrate that 'print' returned 'NULL' using the evalb (evaluate in Boolean domain) command > evalb(res = NULL); true o END ASIDE o Continuing with our 'for' examples ... -------------------------------------- o myfor2: Uses negative loop increment -------------------------------------- > op(myfor2); proc(n::integer) local i; for i from n by -1 to 1 do print(i) end do end proc > myfor2(5); 5 4 3 2 1 o Try to guess what the following will do before you execute it > myfor2(-10); o So "if there is nothing for the loop to do" then the loop does not execute at all (i.e. 0 times) -------------------------------------- o myfor3: Uses loop increment of 3 -------------------------------------- > op(myfor3); proc(n1::integer, n2::integer) local i; for i from n1 by 3 to n2 do print(i) end do end proc > myfor3(10, 20); 10 13 16 19 -------------------------------------- o Here's a more interesting example: o myfactorial: Computes n! -------------------------------------- o Definition from source file ######################################################################## # Computes n! (Doesn't check that n >= 0, left as exercise) ######################################################################## myfactorial := proc(n::integer) # local variables # # i -> loop variable # val -> used to "build up" value to be returned local i, val; # Initialize return value val := 1; # Can start loop from 2 (why?) for i from 2 to n do # Note the structure of the following statement, which is # common is programming: the right hand side of the assignment # is evaluated first, the result of that evaluation is # assigned to 'val': i.e. the statement CHANGES the value # of 'val' val := val * i; end do; # Return the computed value val; end proc; o Display the definition in the maple session > op(myfactorial); proc(n::integer) local i, val; val := 1; for i from 2 to n do val := val*i end do; val end proc o Try a couple of examples > myfactorial(10); 3628800 > myfactorial(100); 933262154439441526816992388562667004907159682643816214685929638952175999932\ 29915608941463976156518286253697920827223758251185210916864000000000000\ 000000000000 o Test to see whether the implementation is correct using maple's built in factorial function > myfactorial(100) - 100!; 0 ------------------------------------------------------------------------- ERROR STATEMENT ------------------------------------------------------------------------- o Syntax error ; or error( then end if; ------------------------------------------------------ o Display the definition of myerror in the maple session > op(myerror); proc(y::numeric, z::numeric) if y <= 0 then error "First argument must be positive" end if; y + z end proc > myerror(3, 7); 10 > myerror(-10, 6); Error, (in myerror) First argument must be positive o AGAIN: Note that when an 'error' statement/command is encountered in a procedure, the string argument supplied to 'error' is prepended with 'Error, (in ) ', the resulting string is output, and the procedure automatically/immediately exits/returns (and in this case there is NO return value, not even NULL) o You will need to use the 'error' statement when implementing some of the procedures in the current homework ------------------------------------------------------------------------- myif3 REVISITED ------------------------------------------------------------------------- o Recall that the purpose of myifC (from last lab's exercise set) is to determine the largest (maximum) of its three arguments o Let's consider a version of this procedure, now called 'max3', which is defined in ~phys210/maplesolns/max3 o From within a terminal window, execute % cd ~phys210/maplesolns % more max3 ############################################################ # Implementation of myifC, renamed max3, that illustrates # use of code "factoring" via definition of "helper" # procedure, max2, which returns maximum of its TWO # arguments. # # Coded this way, the correctness of the implementation # is more manifest and the "gotcha" vis a vis the use # of >= rather than > does not arise. ############################################################ ############################################################ # max2: returns maximum of two numeric values ############################################################ max2 := proc(x1::numeric, x2:: numeric) if x1 > x2 then x1; else x2; end if; end proc; ############################################################ # max2: using max2, returns maximum of three numeric values ############################################################ max3 := proc(x1::numeric, x2::numeric, x3::numeric) max2(max2(x1, x2), x3); end proc; o Examine the testing code for 'max3' that is defined in the file tmax3 (it uses the 'printf' command---slightly advanced topic, see ?printf, or use help menu for more information) % more tmax3 ############################################################ # "Exhaustive" testing of max3, using all permutations # of x1, x2, x3 chosen from 1, 2, 3 ############################################################ read max3; for x1 from 1 to 3 do for x2 from 1 to 3 do for x3 from 1 to 3 do printf("max3(%a,%a,%a)=%a\n", x1, x2, x3, max3(x1, x2, x3)); end do; end do; end do; o From within ~phys210/maplesolns, start a maple session (command-line maple) and read 'tmax3' % cd ~phys210/maplesolns % maple > read tmax3; .... examine the output carefully ... does it appear correct? o Now read 'tmyifCbad', and examine output carefully, comparing to that from 'tmax3' ... What's the difference? > read tmyifCbad; ------------------------------------------------------------------------- EXERCISE ------------------------------------------------------------------------- o In the file '~/maplepgm2/myprocs3' create the following procedure o mysum o Takes a single argument, n which must be integer (use type-checking) o Uses a for loop to compute the sum of the integers from 1 to n o The procedure should check that n is >= 1; if it isn't, it should use the 'error' statement to output an appropriate message and exit o Create testing file for mysum called 'tmysum', o First line should be 'read myprocs3;' o Subsequent line should call mysum with various arguments, valid as well as invalid: use 'print' statements to document what o Can use 'myfactorial' as a guide, but you should implement/type whole procedure yourself. ------------------------------------------------------------------------- FREE TIME TO WORK ON HOMEWORK, DISCUSS TERM PROJECTS -------------------------------------------------------------------------