Ex 7-5

J

jameskuyper

mdh wrote:
....|
When you put all this together, is there some logic as to why those
who wrote the specifications chose to do it this way, or is this
simply something you remember? I **though** I had figured out some
reason earlier, but I know better than to give my theory!!!! :)

I don't know the exact reason, but by reverse engineering I think that
the idea was that one or more white space characters in the input
would be used to delimit fields, but would otherwise be ignored; this
mimics the way that human beings normally read lines of text. I think
it was also intended that format strings be allowed to have extra
white space, as needed, to make them more readable.
The exceptions for the [, c, and n specifiers were made to allow space
characters to be read in, rather than ignored, on those less common
occasions where there was a need to do so.
 
C

CBFalconer

mdh said:
.... snip ...

Ben...one last point of clarification.
K&R (p159) say this.
"scanf ignores blanks and tabs in it's format string. Furthermore,
it skips over white space( blanks, tabs, newlines, etc) as it
looks for input values"

Now, I assume that the space you have will work, but could you
perhaps explain the seemingly contradictory statements.

The format string is NOT the input that scanf is scanning. Also a
%c specification in the format does not skip leading blanks.
 
J

jameskuyper

CBFalconer said:
The format string is NOT the input that scanf is scanning. ...

The cited comment from K&R was about blanks and tabs in the format
string. mdh's question was about a format string (which you snipped)
which did contain a blank. Neither comment was about "scanning". The
format string is indeed input to scanf(), even if it is not what
scanf() is "scanning", and both comments were about what scanf() does
with the format string it receives as input.

So, what exactly was the point of your comment?
... Also a
%c specification in the format does not skip leading blanks.

The format string that you snipped did not have a %c specification. It
did have a %[ specification.
 
R

Ron Ford


[reordered and snipped]
#include <stdio.h>

#define NUMBER '0'

int getop(double *val);
double push(double val);
double pop(void);
void clear(void);

int main(void)
{
int op;
double num;

while ((op = getop(&num)) != EOF) {
switch (op) {
case NUMBER:
push(num);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
num = pop();
push(pop() - num);
break;
case '/':
num = pop();
if (num)
push(pop() / num);
else fprintf(stderr, "Error: division by zero.");
break;
case '#':
push(push(pop()));
break;
case 'c':
clear();
break;
case '=':
printf("\t%g\n", push(pop()));
break;
default:
fprintf(stderr, "Error: unknown operation %c.\n", op);
break;
}
}
return 0;
}


Alright, Ben, I'm back in the saddle and have some time to put toward a
solution here. What you have above is close to every solution I've seen
for this calculator with a couple exceptions. One is that you don't have a
case for '!', but that's no big deal, we can add it. The other is that
you have a differing type for the argument of getop. That's a significant
change.

This little program is getting to be 200 lines, so if we can figure out the
switch control part of it, then we can omit almost all of it until we think
we have a final solution, which would be a good thing. We could simply
leave the case for say '%', to be able to have something that any clc'er
can compile and check behavior for.

Now that I've had a few days to think about it, I would actually like to
end up with a calculator I can use. The feature that I don't think we've
coded for that has reference to the above is how better to manipulate the
stack. My HP something C had a rotate feature that, along with other
operations, allowed me to get whatever stack values I wanted in the first
and second positions. So it is that I would have a relevant value on the
stack, and usually the final calculation would be to rotate the stack, and
what was on the top would be on the bottom, and magically, the other
relevant value was in the second position, ready for the final divide that
would be the answer.

So, I think we need a rotate that does

4
3
2
1
=>
3
2
1
4

and a swap that does

3
2
1
4
=>
3
2
4
1

, and then we'd have something eminently useful.


void clearStack(void); is better (like the others).



I think there is a missing break after clearStack(); The pop() does
not make sense if the stack has been cleared so a fall-though can't
have been intended.

Every other case has a break statement, so I wouldn't see why this one
doesn't need one too.

My big problem with the "solution" is that is does not do what is
intended! I don't interpret the suggestion to rewrite to use scanf
"for the number conversion" to include the use of scanf for the bit it
does badly (reading a single character) and not for the bit it does
well (reading and converting numbers). It seems to be a daft
non-solution.

You claim there's a better sscanf soln waiting to happen here. I don't
disbelieve.

int getop(double *num)
{
int rc;
char s[2];
if ((rc = scanf(" %1[^0-9]", s)) == 1)
return s[0];
else if (rc != EOF && scanf("%lf", num) == 1)
return NUMBER;
else return EOF;
}

If this does the trick, it's a clearly better soln than T&G's. What does s
represent here?

/* This is the simple stack almost straight out of the original */

#define MAXVAL 100

static int sp = 0; /* Next free stack position. */
static double val[MAXVAL];

double push(double f)
{
if (sp < MAXVAL)
return val[sp++] = f;
else {
fprintf(stderr, "Error: stack full.\n");
clear(); /* Following K&R here -- seems reasonable. */
return 0;
}
}

You add static as a storage class modifier. You change the function from
void to double. That would mean that the 101st value gets returned. I'm
not sure that pulling the plug on the whole stack is the best way to deal
with that.

double pop(void)
{
if (sp > 0)
return val[--sp];
else {
fprintf(stderr, "Error: stack empty.\n");
return 0;
}
}

I thought mdh made an error by returning a decimal zero, but that's what
K&R has. Why don't you?


void clear(void)
{
sp = 0;
}

Can you speak to the static nature of sp and this method of clearing the
stack?
 
B

Ben Bacarisse

Ron Ford said:

[reordered and snipped]
#include <stdio.h>

#define NUMBER '0'

int getop(double *val);
double push(double val);
double pop(void);
void clear(void);

int main(void)
{
int op;
double num;

while ((op = getop(&num)) != EOF) {
switch (op) {
case NUMBER:
push(num);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
num = pop();
push(pop() - num);
break;
case '/':
num = pop();
if (num)
push(pop() / num);
else fprintf(stderr, "Error: division by zero.");
break;
case '#':
push(push(pop()));
break;
case 'c':
clear();
break;
case '=':
printf("\t%g\n", push(pop()));
break;
default:
fprintf(stderr, "Error: unknown operation %c.\n", op);
break;
}
}
return 0;
}


Alright, Ben, I'm back in the saddle and have some time to put toward a
solution here. What you have above is close to every solution I've seen
for this calculator with a couple exceptions.

That is because I copied it from K&R as did, I expect, everyone else.
To do the exercises it suffices to change the bits they suggest you
change. Laziness is a virtue in programming.
One is that you don't have a
case for '!', but that's no big deal, we can add it. The other is that
you have a differing type for the argument of getop. That's a significant
change.

All part of the exercise's instructions to "re-write the calculator".
It did not say "re-write getop" so I felt free to make changes in
keeping with the instruction to "use scanf/sscanf to read and convert
the numbers".
Now that I've had a few days to think about it, I would actually like to
end up with a calculator I can use.
, and then we'd have something eminently useful.

Variables would be more useful in my opinion. Another K&R exercise.
Every other case has a break statement, so I wouldn't see why this one
doesn't need one too.

Glad you agree. The pop() causes undefined behaviour without the break;
int getop(double *num)
{
int rc;
char s[2];
if ((rc = scanf(" %1[^0-9]", s)) == 1)
return s[0];
else if (rc != EOF && scanf("%lf", num) == 1)
return NUMBER;
else return EOF;
}

If this does the trick, it's a clearly better soln than T&G's. What does s
represent here?

It is to read a single character using " 1%[...]". The 1 means we
read only one character, but %[...] is like %s -- it needs an array
and stores a null at the end. Hence the 2 bytes.

But thanks for making me go read that standard again! I see that
%[0-9] and %[^0-9] are implementation defined so I will re-write this
as " 1%[^0123456789]". Shame.
/* This is the simple stack almost straight out of the original */

#define MAXVAL 100

static int sp = 0; /* Next free stack position. */
static double val[MAXVAL];

double push(double f)
{
if (sp < MAXVAL)
return val[sp++] = f;
else {
fprintf(stderr, "Error: stack full.\n");
clear(); /* Following K&R here -- seems reasonable. */
return 0;
}
}

You add static as a storage class modifier.

Yes. Is that a problem?
You change the function from
void to double.

I changed it *back*. My K&R has it return the stacked value and I
thought that was better than the change to void by whoever. K&R's
main uses this fact to print push(pop()).
That would mean that the 101st value gets returned.

I don't get this. Pushing 101 consecutive values causes an error
message to be output, the stack to be cleared, and zero to be
returned. The 101st value is not returned.
I'm
not sure that pulling the plug on the whole stack is the best way to deal
with that.

You mean you don't like the clear when the stack overflows? Fine. I
was following K&R so you could see what I had changed and not have to
worry too much about other changes.
double pop(void)
{
if (sp > 0)
return val[--sp];
else {
fprintf(stderr, "Error: stack empty.\n");
return 0;
}
}

I thought mdh made an error by returning a decimal zero, but that's what
K&R has. Why don't you?

Sorry, what is a decimal zero? Do you think 0 is not a good return
for a function returning a double?
Can you speak to the static nature of sp and this method of clearing the
stack?

I prefer sp and val (the stack) to be static because there can be on
only one of each (they are not passed as parameters to the stack
operations) and I'd rather prevent accidental access from other parts
of the program. The functions can, quite reasonably, be used by other
parts of the program -- even other compilation units so the functions
were left with external linkage.

As to the clearing method, how else could it be done?
 
R

Ron Ford

That is because I copied it from K&R as did, I expect, everyone else.
To do the exercises it suffices to change the bits they suggest you
change. Laziness is a virtue in programming.

Laziness is a virtue in *Perl* programming, while masochism is the C norm.


What I suggest we do, Ben, is have two subthreads, one to determine the
characteristics of the swicth mechanism, and the other to to rewrite getop.
We could differentiate the two by a bracketed comment like
[ switch mechanism ] and
[ getop ]


Passersby can thereby comment without having to read 50 K of context. What
I have now compiles but doesn't behave:





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

#define MAXOP 100
#define NUMBER 0
#define TRUE 1
#define FALSE 0



// int getop(char s[]);
int getop(double *val);
// push changed
double push(double val);
double pop(void);
void showTop(void);
void duplicate(void);
void swapItems(void);
void clearStack();
void clear(void);

int main(void)
{

double op2;
char s[MAXOP];
// added in
int op;
double num;

//while((type = getop(s)) != EOF)

while ((op = getop(&num)) != EOF)
{
switch(op)
{
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop()- op2);
break;
case '/':
op2 = pop();
if(op2)
push(pop() / op2);
else
printf("\nError: division by zero!");
break;
case '%':
op2 = pop();
if(op2)
push(fmod(pop(), op2));
else
printf("\nError: division by zero!");
break;
case '?':
showTop();
break;
case '#':
duplicate();
break;
case '~':
swapItems();
break;
case '!':
clearStack();
break;
case '\n':
printf("\n\t%.8g\n", pop());
break;
default:
printf("\nError: unknown command %s.\n", s);
break;
}
}
return EXIT_SUCCESS;
}

#define MAXVAL 100

int sp = 0; /* Next free stack position. */
double val[MAXVAL]; /* value stack. */

double push(double f)
{
if (sp < MAXVAL)
return val[sp++] = f;
else {
fprintf(stderr, "Error: stack full.\n");
clear(); /* Following K&R here -- seems reasonable. */
return 0;
}
}

double pop(void)
{
if (sp > 0)
return val[--sp];
else {
fprintf(stderr, "Error: stack empty.\n");
return 0;
}
}

void clear(void)
{
sp = 0;
}




void showTop(void)
{
if(sp > 0)
printf("Top of stack contains: %8g\n", val[sp-1]);
else
printf("The stack is empty!\n");
}


void duplicate(void)
{
double temp = pop();

push(temp);
push(temp);
}

void swapItems(void)
{
double item1 = pop();
double item2 = pop();

push(item1);
push(item2);
}

/* pop only returns a value if sp is greater than zero. So setting the
stack pointer to zero will cause pop to return its error */

void clearStack(void)
{
sp = 0;
}



// new getop

int getop(double *num)
{
int rc;
char s[2];
if ((rc = scanf(" %1[^0123456789]", s)) == 1)
return s[0];
else if (rc != EOF && scanf("%lf", num) == 1)
return NUMBER;
else return EOF;
}



// gcc -o calc -Wall -std=c99 kr7_5_5.c




If we get our source on the same page, it will help.
 
B

Ben Bacarisse

What I suggest we do, Ben, is have two subthreads, one to determine the
characteristics of the swicth mechanism, and the other to to rewrite getop.
We could differentiate the two by a bracketed comment like
[ switch mechanism ] and
[ getop ]

Bad idea. The main problem with your code below is that it is an
incompatible mix of versions.

double op2;
char s[MAXOP];
// added in
int op;
double num;

//while((type = getop(s)) != EOF)

while ((op = getop(&num)) != EOF)
{
switch(op)
{
case NUMBER:
push(atof(s));

My getop does not put anything into s, so this pushes nothing but
junk.

case '#':
duplicate();

My preference is for push(push(pop());
break;
case '~':
swapItems();

And for general use I'd make this rotate(2) so you can add other
operations that rotate the top n elements.

If we get our source on the same page, it will help.

Yes, stick with one design rather than a hybrid.
 
R

Ron Ford

What I suggest we do, Ben, is have two subthreads, one to determine the
characteristics of the swicth mechanism, and the other to to rewrite getop.
We could differentiate the two by a bracketed comment like
[ switch mechanism ] and
[ getop ]

Bad idea. The main problem with your code below is that it is an
incompatible mix of versions.

double op2;
char s[MAXOP];
// added in
int op;
double num;

//while((type = getop(s)) != EOF)

while ((op = getop(&num)) != EOF)
{
switch(op)
{
case NUMBER:
push(atof(s));

My getop does not put anything into s, so this pushes nothing but
junk.

case '#':
duplicate();

My preference is for push(push(pop());

With another close paren.
And for general use I'd make this rotate(2) so you can add other
operations that rotate the top n elements.

I like that. I hadn't noticed the tilde yet.

Yes, stick with one design rather than a hybrid.

Still doesn't behave:

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

#define MAXOP 100
#define NUMBER 0
#define TRUE 1
#define FALSE 0



// int getop(char s[]);
int getop(double *val);
// push changed
double push(double val);
double pop(void);
void showTop(void);
void duplicate(void);
void swapItems(void);
void clearStack();
void clear(void);

int main(void)
{

double op2;
char s[MAXOP];
// added in
int op;
double num;

//while((type = getop(s)) != EOF)

while ((op = getop(&num)) != EOF)
{
switch(op)
{
case NUMBER:
push(num);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop()- op2);
break;
case '/':
op2 = pop();
if(op2)
push(pop() / op2);
else
printf("\nError: division by zero!");
break;
case '%':
op2 = pop();
if(op2)
push(fmod(pop(), op2));
else
printf("\nError: division by zero!");
break;
case '?':
showTop();
break;
case '#':
push(push(pop()));

break;
case '~':
swapItems();
break;
case '!':
clearStack();
break;
case '\n':
printf("\n\t%.8g\n", pop());
break;
default:
printf("\nError: unknown command %s.\n", s);
break;
}
}
return EXIT_SUCCESS;
}

#define MAXVAL 100

int sp = 0; /* Next free stack position. */
double val[MAXVAL]; /* value stack. */

double push(double f)
{
if (sp < MAXVAL)
return val[sp++] = f;
else {
fprintf(stderr, "Error: stack full.\n");
clear(); /* Following K&R here -- seems reasonable. */
return 0;
}
}

double pop(void)
{
if (sp > 0)
return val[--sp];
else {
fprintf(stderr, "Error: stack empty.\n");
return 0;
}
}

void clear(void)
{
sp = 0;
}




void showTop(void)
{
if(sp > 0)
printf("Top of stack contains: %8g\n", val[sp-1]);
else
printf("The stack is empty!\n");
}




void swapItems(void)
{
double item1 = pop();
double item2 = pop();

push(item1);
push(item2);
}

/* pop only returns a value if sp is greater than zero. So setting the
stack pointer to zero will cause pop to return its error */

void clearStack(void)
{
sp = 0;
}



// new getop

int getop(double *num)
{
int rc;
char s[2];
if ((rc = scanf(" %1[^0123456789]", s)) == 1)
return s[0];
else if (rc != EOF && scanf("%lf", num) == 1)
return NUMBER;
else return EOF;
}



// gcc -o calc -Wall -std=c99 kr7_5_5.c

10 5 - hangs:-(
 
B

Ben Bacarisse

Ron Ford said:
What I suggest we do, Ben, is have two subthreads, one to determine the
characteristics of the swicth mechanism, and the other to to rewrite getop.
We could differentiate the two by a bracketed comment like
[ switch mechanism ] and
[ getop ]

Bad idea. The main problem with your code below is that it is an
incompatible mix of versions.

This is still partly true:
case '\n':
printf("\n\t%.8g\n", pop());
break;

My getop never returns '\n'. It can be modified to do so but it was
always written as an answer to that exercise so it was designed to fit
with K&R's calculator not the one from the wiki or T&G.
default:
printf("\nError: unknown command %s.\n", s);
break;

And s has no (defined) contents here so you get confusing error
messages.

10 5 - hangs:-(

Its probably just the newline issue. 10 5 - ? works as expected.
 
D

David Thompson

Ron Ford <[email protected]> writes:
int Getop(char line[]) is a gemisch of a few things. K&R and T&G use getop
with a small g, which, according to gcc, differs from large G Getop.

Not only gcc. Case matters in C's identifiers.
Nit: but possibly not in C90 for _external_ linkage, which this has.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 
M

mdh

All part of the exercise's instructions to "re-write the calculator".
It did not say "re-write getop"


But thanks for making me go read that standard again!  I see that
%[0-9] and %[^0-9] are implementation defined so I will re-write this
as " 1%[^0123456789]".  Shame.





I think Ben wrote getop as follows:( only changing [^0-9] to
[^012345678] ..as it is implementation dependent)


int getop(double *num){ /* get one operator/operand */
int rc;
char s[2];
if ( (rc=scanf(" %1[^0123456789]", s )) == 1 )
return s[0];
else if ( (rc != EOF) && scanf("%lf", num) == 1)
return NUMBER;
else
return EOF;


I have a couple of questions.


1) getop expects an argument of a pointer to double. If scanf expects
a pointer to store the result of the conversion specifier, why is
"&num" not used instead of num ( which clearly works, but not sure
why)?

2) Does the version of getop handle negative floating point numbers
correctly? If I try and pass -90.98, the "minus" is read as an
operator, and not part of a negative double.

thanks as always.
 
B

Ben Bacarisse

mdh said:
All part of the exercise's instructions to "re-write the calculator".
It did not say "re-write getop"

But thanks for making me go read that standard again!  I see that
%[0-9] and %[^0-9] are implementation defined so I will re-write this
as " 1%[^0123456789]".  Shame.


I think Ben wrote getop as follows:( only changing [^0-9] to
[^012345678] ..as it is implementation dependent)


int getop(double *num){ /* get one operator/operand */
int rc;
char s[2];
if ( (rc=scanf(" %1[^0123456789]", s )) == 1 )
return s[0];
else if ( (rc != EOF) && scanf("%lf", num) == 1)
return NUMBER;
else
return EOF;
}
(and different layout but, yes, that looks like mine)
I have a couple of questions.

1) getop expects an argument of a pointer to double. If scanf expects
a pointer to store the result of the conversion specifier, why is
"&num" not used instead of num ( which clearly works, but not sure
why)?

If it "expects a pointer" and the argument (num) "is a pointer to
double" what is the problem? &num has type double ** for which no
format is exists.

I'll gladly "take a note" -- some people want pointerlyness to be part
of the name -- num_ptr, result_ptr or some such. I am not sure and
for small functions I rarely do this. For one thing the declaration
is right there, and for another C compilers are good a telling you
when you make an indirection error (*num or &num where num is
wanted). Its a debatable issue, but maybe the name is putting you off.
2) Does the version of getop handle negative floating point numbers
correctly? If I try and pass -90.98, the "minus" is read as an
operator, and not part of a negative double.

No, but then neither did the original.
 
M

mdh

}

(and different layout but, yes, that looks like mine)


Sorry Ben...did not paste and copy..so you are correct, it hopefully
had your "feel" :)
If it "expects a pointer" and the argument (num) "is a pointer to
double" what is the problem?  &num has type double ** for which no
format is exists.

 Its a debatable issue, but maybe the name is putting you off.

Nope...just checking to make sure I understand this, before moving on.
So, once "num" is declared as a ptr to double, the use of the
identifier alone ( ie num) is exactly that! :) )

Thanks Ben.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,241
Latest member
Lisa1997

Latest Threads

Top