The Tiny RPN Calculator: Version 1.03 Overview

Extending the Calculator

Version 1.03 enables users to embed program names within programs. Now a program can call another program nested inside of it. This nesting can take place for up to 24 levels! (The maximum number of levels is actually quite arbitrary, as we'll soon see.)

The mechanism for nesting programs is simple and straightforward. In the ProgramManager class we convert the single-integer variable "selection" into an array of 25 integers. We also add an integer flag called level that is designed to index the selections offered in the array. When level = 0, there is no program running in the calculator. As soon as the calculator encounters a program name, it increments level to 1, and proceeds to run the program at that level. If there is a program name within that program, level is incremented to 2, and the calculator selects and runs the program at that level...and so on, stepping up and down through the levels until it's back to level 1, then to level 0, when program execution is complete. To implement this mechanism, the CheckProgramOperations() method was revised and expanded considerably.

When a new program is created, it's simply pushed into the vector<Program> programs variable and subsequently called through its position within the vector. If an existing program is overwritten, a new copy is pushed into the vector, and the old one is "nullified," although it remains within its position. But no matter, for the calculator will always reference the new copy and ignore the old one.

Note too that the ProgramManager object now checks prospective program names for legitimacy, and rejects those that are either numeric literals, or variable names, or operator names, or "keywords" that are reserved for the calculator's own use, such as "log", "sinh", etc. This is achieved through the four new private methods in the ProgramManager class. ... (More detail to follow soon...)

Calling Programs From Other Programs

Writing and running programs on Version 1.03 is similar in nearly every respect to doing same for Version 1.02—the only difference being that now we can call programs from other programs. It should suffice to demonstrate this feature using just one example. We will redo Exercise 1 of Version 1.02; only this time we'll see how to embed all the various programs within a single routine. We begin by creating the programs mycosh, mysinh, and diffsq as before:

pgm
Enter a name for the program: mycosh
Enter a sequence of program instructions; terminate by entering "end": dp exp sw chs exp + 2 / ? end
   Program "mycosh" created.
pgm
Enter a name for the program: mysinh
Enter a sequence of program instructions; terminate by entering "end": dp exp sw chs exp - 2 / ? end
   Program "mysinh" created.
pgm
Enter a name for the program: diffsq
Enter a sequence of program instructions; terminate by entering "end": sw sq ? sw sq ? - ? end
   Program "diffsq" created.

Now we will create a single program, called hyper, that invokes all of these program instructions (along with some judiciously-placed stack functions). This should be easy: the program will consist of the same sequence of instructions we invoked in the final step of Exercise 1. To show that we're very much on the right track, we'll test the hyper program using some of the inputs from that exercise:

pgm
Enter a name for the program: hyper
Enter a sequence of program instructions; terminate by entering "end": dp mycosh sw mysinh diffsq end
   Program "hyper" created.
1.5 hyper
   2.35240961524
   2.12927945509
   5.53383099789
   4.53383099789
   1
e hyper
   7.61012513866
   7.54413710282
   57.9140046261
   56.9140046261
   1
pi 3 * hyper
   6195.82394431
   6195.82386361
   38388234.3489
   38388233.3489
   1

By this time you should appreciate that using custom-built program instructions is really not much different from invoking the native functions built into the calculator. That's one of the concepts that underlies extension of the calculator's capabilities: creating complex instructions out of simpler ones. What you haven't seen yet, however, is a program designed to make the calculator step through more than two levels of routines. Luckily for you, you will have the chance to write such programs when you get to the Exercises in this overview!

Looking Ahead

For the first time, our tiny calculator is showing signs of becoming a mature application. Now if only we didn't have to retype our favorite programs every time we fire up the calculator...hmmm...

Exercises

1) Try creating some programs that have the following names: (a) 49ers (b) sf49ers (c) X (d) x (e) * (f) ** (g) yx (h) xy. Explain why Version 1.03 accepts some of these as program names while rejecting others.

2) Many medications and food supplements come in capsule form. No doubt you can identify what these capsules look like: they're essentially cylinders with a half-sphere appended to each end. For want of a better name we'll call these composite geometric solids capsules. This exercise calls for writing a single program called capsule that computes and outputs the volume and surface area of a capsule, given its overall length and width as user inputs, in that order. Your program should be a composite of other programs used to compute such things as the surface area and volume of a sphere, or of a cylinder. You will also need a program to compute the area of a circle, and another program to store the capsule's dimensions in variables that the other programs can use intelligibly. Test your capsule program using the following duples as entries, representing length and width respectively: (a) 10, 2; (b) 5, 4; (c) 12, 2; (d) 8, 3; (e) 6, 4; (f) 5, 4.8; (g) 7.38, 2.22; (h) 73.8, 22.2; (i) 738, 222.

3) How many levels will the calculator step through in order to run the capsule program? Why?

4) Is the capsule program devised in Exercise 2 the optimal way to calculate the volume and surface area of a capsule? Why or why not? If not, what would you suggest so as to optimize the calculation?

Solutions to Exercises

1) Version 1.03 rejects the entry "49ers" as a program name because according to the method ProgramManager:: IsNumericEntry(), if even just the first character of the name is a digit, this method will interpret the entry as a numeric literal and reject it. The string "sf49ers" is acceptable because it cannot be misconstrued by the aforesaid method as being a numeric literal. The strings "X" and "*" will be rejected because the method ProgramManager::IsVariableOrOperator() will trap the former as being a variable name, the latter as being a math operator. Contrariwise, there is nothing to block the use of "x" as a program name, because the lower-case "x" is not one of the characters marked as invalid by the string invalidChars. Nor will the odd-looking string "**" be rejected, because the ProgramManager:: IsVariableOrOperator() method tests only strings that are one character long (terminating characters excluded). The string "yx" will be rejected as a program name because the method ProgramManager::IsReservedKeyword() will trap it as being the name of a math function (the power function y to the x, to be exact). On the other hand, the string "xy" is not the name of a math function, and there is otherwise nothing to prevent it from being used as a program name.

2) Several solutions are possible, but this one will give you a general idea of how to go about solving the problem. The solution is based on the following formulas, which assume a diameter of d (for spheres and cylinders) and height h (cylinders):

Our solution will also assume that the width of a capsule is equivalent to its diameter, and therefore the length of its cylindrical portion is equal to its overall length minus its overall width:

pgm
Enter a name for the program: sto
Enter a sequence of program instructions; terminate by entering "end": W = L = end
   Program "sto" created.
pgm
Enter a name for the program: acirc
Enter a sequence of program instructions; terminate by entering "end": pi W sq * 4 / end
   Program "acirc" created.
pgm
Enter a name for the program: asph
Enter a sequence of program instructions; terminate by entering "end": acirc 4 * end
   Program "asph" created.
pgm
Enter a name for the program: vsph
Enter a sequence of program instructions; terminate by entering "end": pi W cb * 6 / end
   Program "vsph" created.
pgm
Enter a name for the program: vcyl
Enter a sequence of program instructions; terminate by entering "end": acirc L W - * end
   Program "vcyl" created.
pgm
Enter a name for the program: vcaps
Enter a sequence of program instructions; terminate by entering "end": vsph vcyl + end
   Program "vcaps" created.
pgm
Enter a name for the program: acaps
Enter a sequence of program instructions; terminate by entering "end": asph pi W * L W - * + end
   Program "acaps" created.
pgm
Enter a name for the program: capsule
Enter a sequence of program instructions; terminate by entering "end": sto vcaps ? acaps ? end
   Program "capsule" created.
10 2 capsule
   29.3215314335
   62.8318530718
5 4 capsule
   46.0766922527
   62.8318530718
12 2 capsule
   35.6047167407
   75.3982236862
8 3 capsule
   49.480084294
   75.3982236862
6 4 capsule
   58.643062867
   75.3982236862
5 4.8 capsule
   61.5249505279
   75.3982236862
7.38 2.22 capsule
   25.7018218884
   51.4705973994
73.8 22.2 capsule
   25701.8218884
   5147.05973994
738 222 capsule
   25701821.8884
   514705.973994

3) The calculator will step through four levels of program execution, and do it twice. When the capsule program is first run, the variable level is incremented from zero to one. When capsule calls vcaps, the second level is reached; when vcaps calls vcyl, the third level is reached; and when vcyl calls acirc, the program reaches level four. When the volume of the capsule is calculated and output, the program steps back down, then encounters the program instruction acaps. acaps calls asph, bringing the level to three, and asph calls acirc, the fourth level of execution.

It's possible to trace the levels that a program steps through by exploiting an old, old programming trick frequently used before debugging tools became commonplace, but nevertheless still used fairly often to debug programs. That trick consists of inserting output statements in the middle stages of a program, usually to get intermediate results, to test the values of variables used in a program, and the like. I used a variant of the source file rpn103.cpp to output the following results:

.
.
.
pgm
Enter a name for the program: capsule
Enter a sequence of program instructions; terminate by entering "end": sto vcaps ? acaps ? end
   Program "capsule" created.
10 2 capsule
Level 1 reached.
Level 2 reached.
Level 1 reached.
Level 2 reached.
Level 3 reached.
Level 2 reached.
Level 3 reached.
Level 4 reached.
Level 3 reached.
Level 2 reached.
Level 1 reached.
   29.3215314335
Level 2 reached.
Level 3 reached.
Level 4 reached.
Level 3 reached.
Level 2 reached.
Level 1 reached.
Level 0 reached.
   62.8318530718

Try it yourself: modify the source file rpn103.cpp by adding output statements of your own, then build and run the modified calculator using the programs of your choice. You might also experiment by adding statements that identify the program(s) being called (or returned to) as the calculator skips to the next level upward (or downward). (But if you're afraid to mess with source code, here's the variant that I used.)

4) The answer, of course, depends on what "optimal" means for your application! If "optimal" means "using the fewest computer resources and/or creating the least overhead," then clearly the capsule program is far from optimal, due to all the overhead created by calls to subprograms within programs. Remember, it is possible to write a version of capsule that uses a single series of program instructions, jumping only as far as level one. But what such a program gains in efficiency will be offset, to some degree, by what it loses in terms of readability and comprehensibility. If you program in C++, or any other structured language where function calls are the norm, you know that it's possible to write a large program that contains a bare minimum of functions and function calls. You probably also realize how dreadfully written such programs tend to be (witness the main() routines written by many inexperienced programmers).

On the other hand, it's also possible to go too far in the opposite direction, by subdividing and encapsulating trivially small computations within functions to such a degree that it becomes difficult to trace the course of all the function calls. Clearly, the capsule program tends in that direction, but only to make the point that the Tiny Calculator is now capable of calling and running programs nested within other programs to several levels. Perhaps a more realistic solution—one that strikes a good balance between readability and economical use of resources—executes at two levels rather than four. Such a solution is presented below:

pgm
Enter a name for the program: vcaps
Enter a sequence of program instructions; terminate by entering "end":
pi W cb * 6 / pi W sq * 4 / L W - * + end
   Program "vcaps" created.
pgm
Enter a name for the program: acaps
Enter a sequence of program instructions; terminate by entering "end": pi W sq * pi W * L W - * + end
   Program "acaps" created.
pgm
Enter a name for the program: capsule2
Enter a sequence of program instructions; terminate by entering "end": W = L = vcaps ? acaps ? end
   Program "capsule2" created.
10 2 capsule2
   29.3215314335
   62.8318530718
20 4 capsule2
   234.572251468
   251.327412287