问题
I'm trying to create a calculator in c, which can calculate with priority and get right results for examples like these:
((5+5)/3)*3) -- > 9
((1+2) * 3) -- > 9
These examples my code below can calculate. But for something like this
(2+5) * (2+5)
, my program gives wrong answer.
I'm using 2 stacks. One for operators and one for numbers. It works on this principle:
follows:
((4 - 2) * 5) + 3
--> normal infix expression:+ * - 4 2 5 3
Pseudo code:
Read + (an operation), push it onto the stack,
Read * (an operation), push it onto the stack,
Read - (an operation), push it onto the stack,
Read 4 (a number), the top of the stack is not a number, so push it onto the stack.
Read 2 (a number), the top of the stack is a number, so pop from the stack twice, you get 4 - 2, calculate it (2), and push the result (2) onto the stack.
Read 5 (a number), the top of the stack is a number, so pop from the stack twice, you get 2 * 5, push the result (10) onto the stack.
Read 3 (a number), the top of the stack is a number, so pop from the stack twice, you get 3 + 10, push the result (13) onto the stack.
Nothing left to read, pop from the stack and return the result (13).
Actual code:
#include <stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<string.h>
#define MAXSIZE 102
typedef struct
{
char stk[MAXSIZE];
int top;
}STACK;
typedef struct stack
{
int stk[MAXSIZE];
int itop;
}INT_STACK;
STACK s;
INT_STACK a;
void push(char);
char pop(void);
void display(void);
int main()
{
a.itop = 0;
char string[MAXSIZE],vyb,vyb2;
int cislo1,cislo2,vysledok;
while (gets(string) != NULL){
for(int j = strlen(string); j > 0; j--){
if(string[j] == '*' || string[j] == '/' || string[j] == '+' || string[j] == '-')
push(string[j]);
}
//display();
for(int j = 0; j < strlen(string); j++){
if(isdigit(string[j])&&!(a.itop)){
//display();
char pomoc[2];
pomoc[0] = string[j];
pomoc[1] = '\0';
int_push(atoi(pomoc));
}
else if(isdigit(string[j])&&(a.itop)){
cislo1 = int_pop();
vyb2 = pop();
char pomoc[2];
pomoc[0] = string[j];
pomoc[1] = '\0';
cislo2 = atoi(pomoc);
if(vyb2 == '+')
vysledok = cislo1+cislo2;
else if(vyb2 == '-')
vysledok = cislo1-cislo2;
else if(vyb2 == '*')
vysledok = cislo1*cislo2;
else if(vyb2 == '/')
vysledok = cislo1 / cislo2;
//printf(" v %d",vysledok);
int_push(vysledok);
}
}
printf("%d\n",int_pop());
}
}
/* Function to add an element to the stack */
void push (char c)
{
s.top++;
s.stk[s.top] = c;
//printf ("pushed element is = %c \n", s.stk[s.top]);
}
/* Function to delete an element from the stack */
char pop ()
{
char num = s.stk[s.top];
// printf ("poped element is = %c\n", s.stk[s.top]);
s.top--;
return(num);
}
int empty()
{
if (s.top == - 1)
{
printf ("Stack is Empty\n");
return (s.top);
}
return 1;
}
void display ()
{
int i;
if (!empty)
{
printf ("Stack is empty\n");
return;
}
else
{
printf ("\n The status of the stack is \n");
for (i = s.top; i >= 0; i--)
{
printf ("%c\n", s.stk[i]);
}
}
printf ("\n");
}
void int_push (int c)
{
a.itop++;
a.stk[a.itop] = c;
//printf ("pushed element is = %d \n", a.stk[a.itop]);
}
/* Function to delete an element from the stack */
int int_pop ()
{
int num = a.stk[a.itop];
// printf ("poped element is = %d\n", a.stk[a.itop]);
a.itop--;
return(num);
}
Is there any other way to create a calculator with priority, which can give good answers?
Thanks for your respond
回答1:
Put breakpoints - you'll get the following expression:
+ + * 2 5 2 5
. The problem with that, is your interpreter is interpeting this as (2+5+2)*5
instead of (2+5) * (2+5)
.
Well then, you might be wondering how to solve this. There's no simple single solution - you could either fix your own interpreter or build a whole new mechanic, because the way you build expressions just can't handle more then one pair of parthesises.
For example, you may want to calculate all the values in parnthesises before even building the expression seperatley, possibly using recursion in the case of parenthesiseception - however if you actually choose to use that method, you might want to change the way you work with the expressions entirely, because that's a different approach.
If you need me to show actual code examples to explain this further using parts of the code you made, just ask for it and i'll edit and provide what you need.
Either way, I really advise you to look up working with interpreters in general - you could really learn a lot about analysing strings and working with different inputs, and people even did similar stuff to yours with calculators before
EDIT: you asked for examples, so here you go - this is an example of a completely different method using recursion. This way, you handle a single pair of parenthesises at a time, and thus you won't have the problem you currently do. Note - the source i'm basing this on ( pretty much copy-pasted with edits from the thread and some personal comments ) is from codereview on stack exchange, you can see it here if you're intrested.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getInput(char * in) {
printf("> ");
fgets(in, 256, stdin);
}
int isLeftParantheses(char p) {
if (p == '(') return 1;
else return 0;
}
int isRightParantheses(char p) {
if (p == ')') return 1;
else return 0;
}
int isOperator(char p) {
if (p == '+' || p == '-' || p == '*' || p == '/') return p;
else return 0;
}
int performOperator(int a, int b, char p) {
switch(p) {
case '+': return a+b;
case '-': return a-b;
case '*': return a*b;
case '/':
if (b == 0) { printf("Can't divide by 0, aborting...\n"); exit(1); } // now we dont want the world to expload here do we.
return a/b;
default:
puts("Bad value in switch.\n"); // A replacement which was mentioned in the thread- better have a default response just in case something goes wrong.
break;
}
return 0;
}
char isDigit(char p) {
if (p >= '0' && p <= '9') return 1;
else return 0;
}
int charToDigit(char p) {
if (p >= '0' && p <= '9') return p - '0';
else return 0;
}
int isNumber(char * p) {
while(*p) {
if (!isDigit(*p)) return 0;
p++;
}
return 1;
}
int len(char * p)
{
return (int) strlen(p); // This was bugged in the source, so I fixed it like the thread advised.
}
int numOfOperands(char * p) {
int total = 0;
while(*p) {
if (isOperator(*p)) total++;
p++;
}
return total+1;
}
int isMDGRoup(char *p)
{
for(; *p; p++) // used to be a while loop in the source, but this is better imho. more readable, also mentioned on the thread itself.
{
if (!isDigit(*p) && *p != '/' && *p != '*') return 0;
}
return 1;
}
int getLeftOperand(char * p, char * l) {
// Grab the left operand in p, put it in l,
//and return the index where it ends.
int i = 0;
// Operand is part of multi-*/ group
if (isMDGRoup(p)) {
while(1) {
if (*p == '*' || *p == '/') break;
l[i++] = *p++;
}
return i;
}
// Operand is in parantheses (so that's how you write it! sorry for my bad english :)
if(isLeftParantheses(*p)) {
int LeftParantheses = 1;
int RightParantheses= 0;
p++;
while(1) {
if (isLeftParantheses(*p)) LeftParantheses++;
if (isRightParantheses(*p)) RightParantheses++;
if (isRightParantheses(*p) && LeftParantheses == RightParantheses)
break;
l[i++] = *p++;
}
// while (!isRightParantheses(*p)) {
// l[i++] = *p++;
// }
l[i] = '\0';
return i+2;
}
// Operand is a number
while (1) {
if (!isDigit(*p)) break;
l[i++] = *p++;
}
l[i] = '\0';
return i;
}
int getOperator(char * p, int index, char * op) {
*op = p[index];
return index + 1;
}
int getRightOperand(char * p, char * l) {
// Grab the left operand in p, put it in l,
//and return the index where it ends.
while(*p && (isDigit(*p) || isOperator(*p) ||
isLeftParantheses(*p) || isRightParantheses(*p))) {
*l++ = *p++;
}
*l = '\0';
return 0;
}
int isEmpty(char * p) {
// Check if string/char is empty
if (len(p) == 0) return 1;
else return 0;
}
int calcExpression(char * p) {
// if p = #: return atoi(p)
//
// else:
// L = P.LeftSide
// O = P.Op
// R = P.RightSide
// return PerformOp(calcExpression(L), calcExpression(R), O)
// ACTUAL FUNCTION
// if p is a number, return it
if (isNumber(p)) return atoi(p);
// Get Left, Right and Op from p.
char leftOperand[256] = ""; char rightOperand[256]= "";
char op;
int leftOpIndex = getLeftOperand(p, leftOperand);
int operatorIndex = getOperator(p, leftOpIndex, &op);
int rightOpIndex = getRightOperand(p+operatorIndex, rightOperand);
printf("%s, %c, %s", leftOperand, op, rightOperand);
getchar();
if (isEmpty(rightOperand)) return calcExpression(leftOperand);
return performOperator(
calcExpression(leftOperand),
calcExpression(rightOperand),
op
);
}
int main()
{
char in[256];
while(1) {
// Read input from user
getInput(in);
if (strncmp(in, "quit", 4) == 0) break;
// Perform calculations
int result = calcExpression(in);
printf("%d\n", result);
}
}
来源:https://stackoverflow.com/questions/31104531/calculator-in-c-using-stack