[1+1=2]
OneAndOneIs2

Sun, May 04, 2008

[Link][Icon]K&R exercise 4.4

Moving swiftly on: Add more commands to do stuff with the stack. Simplest is, of course, best: Single character commands, here we come!

I went for ? as a 'print topmost' and it's simple enough: Instead of just 'popping' the value, you assign it to the 'op2' variable that we already have for remembering popped numbers, and then you can 'push' it back to the stack after printing it. As an added bonus, you can use it mid-expression to print out values as you go along:

10? 4 -? 3 -? 2 -? 1 -
        10
        6
        3
        1
        0

Duplication is easy: Pop it once, push it twice. It's an easy way to do exponential maths:

1?d+?d+?d+?d+?d+
        1
        2
        4
        8
        16
        32

Swapping them, again not hard, but you DO need another variable: Unlike swapping two values in an array, which only needs one, the stack loses its value when you 'pop' it.

Or so it seemed.

But I didn't like all the inefficiency. What's the point of adding variables to remember things in main() that you've just told pop() to forget, when you can just modify 'pop' so it's more selective? All this poncing about with adding variables in main() to remember because pop() is brainlessly forgetting... No. This is a perfect example of adding overall complexity to keep a small amount of simplicity.

I decided to do a bit more work to get a more efficient and versatile chunk of code. I went back to the drawing board, undid the changes I'd made to the code, and went to meddle with the pop() function instead.

Simple enough: First, some #define's to make the code more clear:

#define LOSE	0
#define KEEP	1
#define SWAP	2
#define CLEAR	3

Then a find&replace to change all existing instances of 'pop()' to 'pop(LOSE)' and quickly change 'double pop(void)' to 'double pop(int)'.

pop() can now be passed arguments to make it more intelligent about what it does. If it's passed a LOSE when called, it 'pops' as usual, but if it gets a KEEP, it 'pops' without removing a value from the stack. Etc. etc.

Time to re-write my new functionality taking advantage of the better pop(). And the code's much nicer now: Instead of '?' doing a pop-to-variable + print-variable + push-variable, it just prints with a lossless-pop. Duplication just pushes a lossless pop. Swapping is done by the function that has access to the array, and clearing is done in pop rather than by adding yet another function.

Considering the C answer book uses the same 'pop to variables' approach I started with, I think I've actually done pretty well here: I've wound up with less calculations and no need for an extra 'clear' function to be added. Mine does everything through a better pop()

Smug mode :o)

The obligatory test: I entered this string and then pressed enter until it emptied the stack. Looks fine to me!

99?99?99?c1 2 4 3 s 5 6 d
        99
        99
        99
        Stack cleared
        6

        6

        5

        4

        3

        2

        1

error: stack empty
        0

Innit marvellous?

[More:]

#include <stdio.h>
#include <stdlib.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];

        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", pop(LOSE));
                        break;
                case '?':
                        printf("\t%.8g\n", pop(KEEP));
                        break;
                case 'd':
                        push(pop(KEEP));
                        break;
                case 'c':
                        pop(CLEAR);
                        printf("\tStack cleared\n");
                        break;
                case 's':
                        pop(SWAP);
                        break;
                default:
                        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;
                }
                else if (i == CLEAR) {
                        sp = 0;
                        return 0;
                }
                else
                        return 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 (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;
}
2 comments • Categories: Omni, Programming

Comments:

Comment from: alison [Member] · http://www.creativehedghog.com
heh, this is cool. :)
PermalinkPermalink 05/05/08 @ 02:36
Comment from: hari [Member] Email · http://hari.literaryforums.org
No offence, but stacks are so... boring. :o

Besides they've already been implemented by so many standard libraries and are much stabler and more feature-rich than custom-made stacks, what's the use of learning them all over again except as a school course?

I feel it's more interesting using stacks for some real purpose than to reimplement stacks in C.
PermalinkPermalink 05/05/08 @ 05:26

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

May 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