[1+1=2]
OneAndOneIs2

Fri, May 09, 2008

[Link][Icon]K&R 4.6

Add the ability to use variables to your calculator.

In a stroke of genius, I initially created a way to assign variables, but forgot to also install a way of calling them. Sigh.

The C answer book redeemed itself a little. After using strcmp in the previous example, when we're still 30-odd pages away from learning its existence, I was not impressed with it. But I thought I was going to have to replace the switch statement with if-else's to be able to do case 'A': case 'B': case 'C':... etc, but it made me realize that putting an 'if' into the 'default:' would solve this one far more easily.

I was also going to create 26 separate variables in the code for the 26 variables in the code, because I hadn't thought enough about it. Creating one array of 26 was of course a much better solution.

Not sure I'll bother with any more of the coding exercises before moving onto the next section.. none leap out at me as particularly interesting. We shall see...

[More:]

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAXOP   100
#define NUMBER  '0'
#define LOSE    0
#define KEEP    1
#define SWAP    2
#define CLEAR   3

int getop(char []);
void push(double);
double pop(int);

int main ()
{
        int type;
        double op2;
        char s[MAXOP];
        double v[26];
        double lastval = 0;

        for (op2 = 0; op2 < 26; op2++)
                v[(int) op2] = 0;

        printf("Welcome to Dominic's reverse Polish notation calculator\n");
        printf("Commands:\n\t+ - * / %% are standard\n");
        printf("\t? displays topmost stack value\t-\td duplicates topmost value\
n");
        printf("\tx clears the stack\t-\te exchanges the two topmost values\n");

        printf("\ts, c, t perform sin, cos, and tan operations respectively\n");

        printf("\tp raises to the power of the topmost number the preceeding num
ber\n");
        while ((type = getop(s)) != EOF) {
                switch (type) {
                        case NUMBER:
                                push(atof(s));
                                break;
                        case '+':
                                push(pop(LOSE) + pop(LOSE));
                                break;
                        case '*':
                                push(pop(LOSE) * pop(LOSE));
                                break;
                        case '-':
                                op2 = pop(LOSE);
                                push(pop(LOSE) - op2);
                                break;
                        case '/':
                                op2 = pop(LOSE);
                                if (op2 != 0.0)
                                        push(pop(LOSE) / op2);
                                else
                                        printf("error: zero divisor\n");
                                break;
                        case '%':
                                op2 = pop(LOSE);
                                if (op2 != 0.0)
                                        push((int) pop(LOSE) % (int) op2);
                                else
                                        printf("error: zero divisor\n");
                                break;
                        case '\n':
                                printf("\t%.8g\n", (lastval = pop(LOSE)));
                                break;
                        case '?':       /* Display topmost stack item */
                                printf("\t%.8g\n", (lastval = pop(KEEP)));
                                break;
                        case 'd':       /* Duplicate topmost stack item */
                                push(pop(KEEP));
                                break;
                        case 'x':       /* Clear stack */
                                pop(CLEAR);
                                printf("\tStack cleared\n");
                                break;
                        case 'e':       /* Switch topmost two stack items */
                                pop(SWAP);
                                break;
                        case 's':
                                push(sin(pop(LOSE)));
                                break;
                        case 'c':
                                push(cos(pop(LOSE)));
                                break;
                        case 't':
                                push(tan(pop(LOSE)));
                                break;
                        case 'p':
                                op2 = pop(LOSE);
                                push(pow(pop(LOSE),op2));
                                break;
                        case 'v':
                                push(lastval);
                                break;
                        case '=':
                                op2 = getop(s);
                                if (op2 >= 'A' && op2 <= 'Z')
                                        v[((int) op2 - 'A')] = pop(LOSE);
                                else
                                        printf("\tBad variable name\n");
                                break;
                        default:
                                if (type >= 'A' && type <= 'Z')
                                        push(v[(type - 'A')]);
                                else
                                        printf("error: unknown command %s\n", s)
;
                                break;
                }
        }
        return 0;
}

#define MAXVAL  100

int sp = 0;
double val[MAXVAL];

void push(double f)
{
        if (sp < MAXVAL)
                val[sp++] = f;
        else
                printf("error: stack full, can't push %g\n", f);
}

double pop(int i)
{
        double j;

        if (sp > 0) {
                if (i == LOSE)
                        return val[--sp];
                else if (i == KEEP)
                        return val[(sp - 1)];
                else if (i == SWAP) {
                        j = val[(sp - 1)];
                        val[(sp - 1)] = val[(sp - 2)];
                        val[(sp - 2)] = j;
                        return 0.0;
                }
                else if (i == CLEAR) {
                        sp = 0;
                        return 0.0;
                }
                else
                        return 0.0;
        }
        else {
                printf("error: stack empty\n");
                return 0.0;
        }
}

#include <ctype.h>

int getch(void);
void ungetch(int);

int getop(char s[])
{
        int i,c;

        while ((s[0] = c = getch()) == ' ' || c == '\t')
                ;
        s[1] = '\0';
        if (!isdigit(c) && c != '.' && c != '-')
                return c;       /* Not a number */
        i = 0;
        if (c == '-') {         /* Deal with negative numbers */
                if (isdigit(c = getch()) || c == '.')
                        s[++i] = c;
                else {
                        if (c != EOF)
                                ungetch(c);
                        return '-';
                }
        }
        if (c == '-') {         /* Deal with negative numbers */
                if (isdigit(c = getch()) || c == '.')
                        s[++i] = c;
                else {
                        if (c != EOF)
                                ungetch(c);
                        return '-';
                }
        }
        if (isdigit(c))         /* Collect integer part */
                while (isdigit(s[++i] = c = getch()))
                        ;
        if (c == '.')           /* Collect fraction part */
                while (isdigit(s[++i] = c = getch()))
                        ;
        s[i] = '\0';
        if (c != EOF)
                ungetch(c);
        return NUMBER;
}

#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;

int getch(void)
{
        return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
        if (bufp >= BUFSIZE)
                printf("ungetch: too many characters\n");
        else
                buf[bufp++] = c;
}
6 comments • Categories: Omni, Programming

Comments:

Comment from: alison [Member] · http://www.creativehedghog.com
wait- I don't get this. so, you have an array of "methods" (excuse my java) and run whichever one gets picked? I'm not sure this is even correct coding.

Life To Do List- get my Java Cert. (missed it by 10 questions in 2004.)
PermalinkPermalink 09/05/08 @ 21:51
Comment from: oneandoneis2 [Member] · http://geekblog.oneandoneis2.org/
Sorry, I don't speak Java..

The switch is like a big if-else chain, only more limited. Which makes it a bit of a pain to code, but more efficient once the compiler gets hold of it because it can do clever things to make it work better.

It didn't occur to me to put if-else's into a switch before, but in theory its no worse than nested ifs.

Not sure if that answers your question or not, tho...
PermalinkPermalink 09/05/08 @ 22:03
Comment from: Vincent Povirk [Visitor] Email
Yep, any time a solution involves writing 26 copies of exactly the same code with a simple substitution, you should instinctively look for a better way (I tend to draw the line at around 3).

Your use of op2 (a double variable) to store the index into an array is very strange. Don't be afraid to add another int variable if you're working with ints; compiler optimizations can combine variables that aren't used at the same time, and avoiding the conversion between floating point and integer types is worthwhile.
PermalinkPermalink 09/05/08 @ 22:10
Comment from: oneandoneis2 [Member] · http://geekblog.oneandoneis2.org/
Yeah, I'm rather ambivalent about the whole "op2 as int" thing.. on the one hand, it's inelegant and not what the variable's there for. On the other.. well.. it works fine.

I can't quite decide if it's worse to use a double as an int than to code a double AND an int when you don't really need to.. I'll have to look into compiler optimizations someday! :o)
PermalinkPermalink 09/05/08 @ 22:39
Comment from: tuxdev [Member] Email
Nice push-down automata implementation. I have a simple challenge for you from this: write the calculator in PN rather than RPN and use recursion to effectively use the system stack to do the work your manual stack does in the RPN version. It's a lot cleaner cause you don't have all the cruft that only simulates a machine.

For the int-vs-double thing, perhaps you can use a union for that. Of course, you need another int to keep track of what kind of thing is in the union, which kind of defeats the whole point when you've only got two kinds of things to deal with.
PermalinkPermalink 09/05/08 @ 23:06
Comment from: hari [Member] Email · http://hari.literaryforums.org
If you're writing an advanced calculator (which is close enough to a programming language of its own), you will inevitably need to use a lexical analyzer with full-fledged regular expression checking.

Why don't you give Flex a look?
http://www.gnu.org/software/flex/

Flex can generate C code from a "rules file" for lexical analysis.

This kind of "if-else" blocks has its limits. You cannot push your calculator beyond a point. You have to "look under the hood" :-p
PermalinkPermalink 10/05/08 @ 05:37

Leave a comment:

Your email address will not be displayed on this site.
Your URL will be displayed.

Allowed XHTML tags: <p, ul, ol, li, dl, dt, dd, address, blockquote, ins, del, span, bdo, br, em, strong, dfn, code, samp, kdb, var, cite, abbr, acronym, q, sub, sup, tt, i, b, big, small>
(Line breaks become <br />)
(Set cookies for name, email and url)
(Allow users to contact you through a message form (your email will NOT be displayed.))

Categories

August 2008
Mon Tue Wed Thu Fri Sat Sun
 << <   > >>
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

Search

Misc

XML Feeds

What is this?
eXTReMe Tracker

Valid XHTML 1.0 Transitional

Valid CSS!

[Valid RSS feed]

powered by
b2evolution

blank