|
Sun, May 04, 2008
![[Link]](http://geekblog.oneandoneis2.org/img/chain_link.gif)
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?
#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;
}
| 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 | |