During the summer of 2009 I determined that it was time for me to review the C programming language, having first learned it about 20 years ago. I had never read The C Programming Language (2nd ed.) by Brian Kernighan and Dennis Ritchie (New Jersey: Prentice Hall, 1988), and as many programmers consider it to be not just a seminal sourcebook on C but also the definitive language reference for ANSI C, I decided to undertake my review there. As I worked my way through Chapter 4, "Functions and Program Structure," I encountered on pages 76-79 an interesting series of exercises to create a simple command-line RPN (Reverse Polish Notation) calculator.
Although I was very intrigued by the possibilities of the calculator, I found its implementation in C to be problematic. Some of the functions were quite lengthy (especially considering that this chapter was to have been an introduction to functions!), and the stack at the heart of the calculator was implemented using external variables, which I thought to be awkward and unnecessary. I was also put off by the surfeit of character input functions in this program. "Why should all the difficulties attending to C-style string inputs be allowed to get in the way of implementing this calculator?" I thought. Even the solutions to the exercises, proposed in The C Answer Book (2nd ed.) by Clovis L. Tondo and Scott E. Gimpel (New Jersey: Prentice Hall, 1989, pp. 73-92), posed significant problems. Although the solution to each of the eight or so exercises was workable and effective, there was never any point at which the authors incorporated all of them in a single program. When I tried to combine them myself, I found these solutions to be sufficiently ad hoc that they could not be made compatible without significant revisions to the entire program.
For the last several years I have been strongly biased toward object-oriented programming techniques. Therefore, I began to wonder out loud whether it might be better (or even fruitful) to tear it all down and start over, by writing a more object-oriented implementation of the calculator using C++ instead. The program that I present on these pages is the result. Aside from attempting to organize the code more intelligibly, the other major objectives of using C++ include eliminating all global variables, and exploiting the power of the string class included in the ANSI/ISO C++ standard, so that I could do away with all such functions as getch(), ungetch(), getline(), getop(), etc.
I have now written seven drafts of the program, each a significant expansion of the draft that preceded it, each adding new features to the calculator to increase its power and flexibility. By the second draft, the calculator had gone well beyond the capabilities of the one presented by Kernighan and Ritchie, and had actually become a programmable calculator. (Crudely programmable, with a capacity for storing and invoking only one program, but programmable nonetheless.) After seven drafts the calculator remains small, even tiny (the executable for Linux is just 73.3 KB), but has evolved into quite a real-number powerhouse, with a potentially wide array of application domains. By building this code around just four classes representing the major components of the calculator—programs, a program manager, a stack, and the calculator itself—I was able to achieve progress very, very rapidly. As the work progressed, I saw a lot of potential in publishing the seven drafts as a kind of online tutorial that demonstrates how to develop, grow, and maintain fairly large programs using an object-oriented language like C++. It is hoped that students of the language—students just like myself, since I too am learning new things all the time—will have much to gain from reviewing this tutorial, and derive pleasure from using the calculator.
If you have experience using older Hewlett-Packard calculators (or newer models from HP's top of the line), chances are you're not only aware of Reverse Polish Notation (also known as "Postfix Notation" or "RPN"); you're most likely an impassioned advocate as well! Anyone who uses a calculator to any considerable extent owes it to him- or herself to learn and master this elegant yet extremely efficient calculation method. Happily, even if you have no experience with RPN, using the Tiny RPN Calculator is actually a great way to start. And if you have used RPN calculators, you may discover that once you become familiar with the range of available math and stack functions available in this calculator, and feel comfortable typing them onto the command line, calculating on the command line compares very favorably to fumbling around for the correct keys (and keystroke sequences) on your hand-held.
It does behoove us to review a few things in advance about how RPN calculation works with respect to this calculator. At the same time, those who are new to RPN will get some feel for how it all "comes together" just by studying the examples in this section. (If you'd like to read some introductory materials first, the articles published by Hewlett-Packard, and by Calculator.org, provide a good practical basis. For a more theoretical understanding of RPN, see Wikipedia's entry on the subject.) First of all, a command-line calculator will not be of much use to anyone if it doesn't provide a way to display the results of a calculation. The Tiny Calculator achieves this with the ? operator. Here's how it works: To calculate the sum of 3 and 4, we write the operands 3 and 4 in succession (separated by spaces, of course), followed by the operator + which is postfixed to the operands 3 and 4, followed by the ? operator to yield the result on-screen, and lastly, a press of the <Enter> key, as shown below:
3 4 + ? <Enter> 7
From this point forward, I will assume you know that every sequence of instructions concludes with a press of the <Enter> key. With this understanding in mind, we will rewrite the preceding example as follows:
3 4 + ? 7
From here it's simply a matter of acquiring more practice, by gradually moving to more interesting examples. (Interestingly, the efficiency and ease of using RPN tend to become more and more apparent as calculations become more and more sophisticated.) Suppose we wish to evaluate the algebraic expression 3 + 7/4 + 6. Because multiplication and division take precedence over addition and subtraction, we understand that division of 7 by 4 should occur before the two sums are taken. Here is the sequence of instructions for properly executing the evaluation using RPN:
3 7 4 / + 6 + ? 10.75
On the other hand, if we wish to subvert the order of operations performed, and evaluate the expression (3 + 7) / (4 + 6) instead, it's clear that, in algebraic terms, we'll need to use parentheses or some other bracketing device to accomplish the subversion. Nevertheless, here is the way the subversion is achieved using RPN:
3 7 + 4 6 + / ? 1
If you're new to RPN, you may be amazed to discover that this evaluation requires no parentheses or bracketing devices of any kind. That's because RPN doesn't need them! The reason is that in RPN, the scope of a binary operator such as +, -, *, or / is always the pair of values that immediately precedes the operator. (Similarly, we will see that in RPN, a math function such as chs, sq, or log always takes as its sole argument the value that immediately precedes the function name.) About these points there is simply no ambiguity. For newcomers to RPN, the trick lies in the acquired ability to see clearly the value(s) being operated upon.
Now let's use one or two of the calculator's built-in math functions to compute the area of a circle whose radius is 3.3 units. Here is the sequence of instructions to do just that:
3.3 sq pi * ? 34.2119439976
Now let's calculate the volume of a cylinder with a radius and height of 2.4 and 3.5 units, respectively:
2.4 sq pi * 3.5 * ? 63.3345078964
Note also that since multiplication is commutative, associative, and distributive, we could as well have written this sequence of instructions as follows, and yielded the same result:
2.4 sq pi 3.5 * * ? 63.3345078964
The ? operator can be invoked as many times as necessary to display the intermediate results of a calculation. For example, let's say we want to compute the volume of the cylinder as before, except that we'd like to check on the area of its base beforehand. This we can do very simply, by writing the sequence of instructions as follows:
2.4 sq pi * ? 3.5 * ? 18.0955736847 63.3345078964
Note here the first occurrence of ? in the instruction sequence, right after the sequence that calculates the area of the circle comprising the cylinder's base. This occurrence instructs the computer to display this intermediate result on the first line beneath the instruction sequence. The second occurrence of ? at the end of the sequence relays a similar instruction to display the volume of the cylinder on the next line below.
This section is intended merely to give a brief foretaste of how RPN has been made to work with respect to the Tiny RPN Calculator. We will survey many, many more examples as we work through each of the seven versions of this program. For now, though, it's worthwhile to be mindful of two important points. First, note that whereas a sequence of calculation instructions always begins on the far left of your screen, the results of the calculation are always presented on successive lines and indented three spaces. Second, newcomers to RPN should note the absence of =, the "equals sign." In general, the RPN method does not utilize this sign to denote equality between expressions on the left- and right-hand sides of a calculation. On the contrary, the calculator uses the operator = to assign values to variables that are built into its memory. We'll see numerous examples of the assignment operator = in action as we investigate Version 1.00 of the calculator.
Because I'm providing every significant draft of this program that I've ever produced, I've chosen to package each version as a single file of source code. Please be aware that this is contrary to traditional practice where packaging of large programs is concerned. If I were presenting only the final draft, I would have broken that file into modules instead, which is more customary. By using a single file for each draft, I'm trying to avoid a cumbersome array of source-code files, and the need for multiple directories in which to house them. Please note too that I've given each source-code file an extension of .txt to obviate browser difficulties, chiefly with Microsoft Internet Explorer. To build each file, simply copy and paste it into a new document, but of course save the document with the .cpp extension before compiling.
Once you have built each version from source, use your shell program to move to the folder in which the executable is located, and invoke the program by name as appropriate for your operating system. If you're a Linux user, for instance, and you've named your executable "rpn", your shell window may look something like the following:
you@your-computer:~/Programming/C++/Calculator$ ./rpn A Tiny RPN Calculator (;-}= v1.00
at which point you'll be up and running. (Of course, you can always write a shell script (or a "batch file" in Windows) to automate this process for you.) Once you're in, experiment with the program. Start with simple calculations, whether from here or from other sources; then by all means devise your own examples. It is strongly recommended that you build and run every version of the program, and become familiar not just with its features but also the source code that implements them, before moving on to the next version. It is also recommended that you build each version within its own folder in your directory structure. That's because Versions 1.04 through 1.06 will create and store program files within the same folder that contains the executable, and the program files that pertain to one version may contain commands that cannot be accessed by a previous version of the calculator.
Finally, let me emphasize that this is an open-source project. You are free to use, modify, and distribute this source code within the guidelines specified by the GNU General Public License, Version 3. So don't be afraid to hack it, whack it, smack it, improve it, and leave it in better condition than when you found it! It would be much appreciated, however, if you take care to acknowledge me and/or XGB Web and Software Design with an appropriate citation and/or a link from your website, and notify me as to any changes you've implemented in the code. And above all, don't fail to have fun! If programming isn't fun, then what's the point?
I wish to express my gratitude to Dr. Roger Whitney, a professor of mathematics and computer science at San Diego State University, for providing very probing, insightful comments and criticisms of an early draft of this program. I trust that by the time he reviews this online edition, he'll find it to be in vastly better shape than before. However, it goes without saying that any defects that remain, whether in the program or this exposition of it, are strictly my own.