|
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?
![[Link]](http://geekblog.oneandoneis2.org/img/chain_link.gif)
Finally, I have the time and the inclination both at once to write some C.
4.3 asks you to add the modulus operator % and provision for negative numbers.
The modulus operator takes you back to early schooldays when you hadn't learned about decimals, when 42 divided by 10 was 4 remainder 2. - The modulus gives the remainder, 42 % 10 = 2
I was going to do the modulus in a complicated way: For i % j, I was going to do:
a = i / j
i - (a * j)
So, with the 42 % 10, you'd get:
a = 42/10 = 4
42 - (4 * 10) = 42 - 40 = 2
Which would have worked. But then it occurred to me, What the Hell am I thinking, just use the modulus operator in the code, moron! so I just copied the division code and replaced / with %
Heh.
It didn't work.
invalid operands to binary %
That stumped me.
Then I went back to those schooldays I mentioned earlier. We used 4 remainder 2 because we didn't have decimals. What was the code passing to %? Doubles. Double-precision floating point numbers. Decimal numbers. No remainders.
I've never had to use a cast before. But replacing push(pop() % op2); with push((int) pop() % (int) op2); fixed the problem for me.
Trivial, really.. Such simple little mistakes make for such buggy software :o)
Next, adding negative number support. Tricky. You have to make it differentiate between a - sign meaning a minus number, and a - sign meaning "subtract". Same character, different meaning. Hmmm...
Can't be done in main(), we'll have to differentiate in the getop() function instead.
To start with, obviously, one must change the line that says 'if this isn't a digit or a period, it's not a number, so return it as-is' to instead say 'if this isn't a digit or a period or a minus sign, it's not a number, so return it as-is'
Then you have to decide what to do if it's a minus sign.
First, I was going to go the simple route: If it's a minus sign followed by a space, it means 'subtract', otherwise it's a minus number.
Then I realized that you can have a minus followed by another minus, or a plus, or a divide, or...
Scrub that. We need to do it properly: If the minus sign is followed by a number or a period.
If it is, it's a minus number. If it isn't, it's a subtract. So return a '-'
Oh, and put whatever followed into the buffer, it might be important.
Oh, unless it's an EOF.
Sigh. I didn't catch that last one - the C answer book pointed it out to me. Oh well, live and learn!
All done, I compiled and got an error message. I'd left out a }
Out of practice? Moi?
Fixed it, and it compiled without issue. As test of the new functionality, I decided to try the sum (103 % 10)(-10 - -14) and see if I got 12 as the answer.
(103 % 10)*(-10 - -14)
[(103 % 10) = 3 ] * [(-10 - -14) = (-10 + 14) = 4]
3 * 4 = 12
So, here goes:
$ gcc -Wall polish43.c -o polish
$ ./polish
103 10 % -10 -14 -*
12
Woohoo! :o)
Not the most trouble-free bit of coding I ever did. I'm badly out of practice in thinking through programming logic, and doing things like brackets properly. But I got it all done in the end, which is what counts at the moment...
I also had to look up the sed command I'd aliased as the 'blogify' command on my old laptop to be able to C&P the code to here, so here it is while I remember:
sed -e 's/</\</g' -e 's/>/\>/g' filename
| 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 | |