文字列を計算するプログラムを考えたいと思います。
いろいろな実装が考えられると思うので、みんながどんな実装をするか知りたいです。
条件
・与えられる数式の長さは「常識的な範囲」
・実数で計算する
・演算子は、^(累乗)、*(掛け算)、/(割り算)、+(足し算)、-(引き算)
・演算子の優先順位は、高い順に()→^(累乗)→*/→+-
・同じ優先順位なら累乗は右から計算、その他の演算子は左から計算する
・足りない部分は適当(適切)に決めていいです
自分はこんな感じで書きました。
追加した条件は
・式に余計な空白は入らない
・eがついた実数には対応しない
という感じです。
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <string>
#include <cstdlib>
#include <cmath>
#include <cctype>
using namespace std;
//作成日 2012/5/27
enum operator_type {
OPERATOR_NUMBER,
OPERATOR_ADD,
OPERATOR_SUB,
OPERATOR_MUL,
OPERATOR_MINUS,
OPERATOR_DIV,
OPERATOR_POW,
OPERATOR_KAKKO_START,
OPERATOR_KAKKO_END
};
struct element_t {
double value;
operator_type opType;
};
int getYusendo(operator_type op,bool isLeft) {
int yusendo=0;
bool isLeftFirst=true;
switch(op) {
case OPERATOR_KAKKO_END:
yusendo=0;
isLeftFirst=false;
break;
case OPERATOR_MINUS:
yusendo=1;
isLeftFirst=false;
case OPERATOR_POW:
yusendo=2;
isLeftFirst=false;
break;
case OPERATOR_MUL:
case OPERATOR_DIV:
yusendo=3;
isLeftFirst=true;
break;
case OPERATOR_ADD:
case OPERATOR_SUB:
yusendo=4;
isLeftFirst=true;
break;
case OPERATOR_KAKKO_START:
yusendo=5;
isLeftFirst=true;
break;
default:
return -1;
}
return yusendo*10+(isLeftFirst==isLeft?0:1);
}
operator_type getOperatorType(char c,bool expectValue) {
switch(c) {
case '+': return OPERATOR_ADD;
case '-': return expectValue?OPERATOR_MINUS:OPERATOR_SUB;
case '*': return OPERATOR_MUL;
case '/': return OPERATOR_DIV;
case '^': return OPERATOR_POW;
case '(': return OPERATOR_KAKKO_START;
case ')': return OPERATOR_KAKKO_END;
default : return OPERATOR_NUMBER;
}
}
bool strcalc(double& result,string expression) {
vector<element_t> reversePorland;
stack<operator_type> operatorStack;
stack<double> calcStack;
bool expectValue=true;
operator_type opType;
for(int i=0;i<expression.length();i++) {
opType=getOperatorType(expression[i],expectValue);
if(opType==OPERATOR_KAKKO_END) {
//かっこの終わり
while(!operatorStack.empty() &&
operatorStack.top()!=OPERATOR_KAKKO_START) {
element_t nowElement;
nowElement.value=0;
nowElement.opType=operatorStack.top();
reversePorland.push_back(nowElement);
operatorStack.pop();
}
if(operatorStack.empty())return false;
operatorStack.pop();
expectValue=false;
} else if(opType==OPERATOR_KAKKO_START) {
//かっこの始まり
operatorStack.push(OPERATOR_KAKKO_START);
expectValue=true;
} else if(opType!=OPERATOR_NUMBER) {
//その他の演算子
while(!operatorStack.empty() &&
getYusendo(operatorStack.top(),true)<getYusendo(opType,false)) {
element_t nowElement;
nowElement.value=0;
nowElement.opType=operatorStack.top();
reversePorland.push_back(nowElement);
operatorStack.pop();
}
operatorStack.push(opType);
expectValue=true;
} else {
//数値
if(!expectValue)return false;
if(!isdigit(expression[i]) && expression[i]!='.')return false;
int j;
bool dotExist=false;
for(j=i;j<expression.length();j++) {
if(isdigit(expression[j]))continue;
else if(expression[j]=='.') {
if(dotExist)break; else dotExist=true;
} else break;
}
element_t nowElement;
nowElement.value=atof(expression.substr(i,j-i).c_str());
nowElement.opType=OPERATOR_NUMBER;
reversePorland.push_back(nowElement);
i=j-1;
expectValue=false;
}
}
if(expectValue)return false;
while(!operatorStack.empty()) {
if(operatorStack.top()==OPERATOR_KAKKO_START)return false;
element_t nowElement;
nowElement.value=0;
nowElement.opType=operatorStack.top();
reversePorland.push_back(nowElement);
operatorStack.pop();
}
for(int i=0;i<reversePorland.size();i++) {
if(reversePorland[i].opType==OPERATOR_NUMBER) {
calcStack.push(reversePorland[i].value);
} else if(reversePorland[i].opType==OPERATOR_MINUS) {
double now;
if(calcStack.empty())return false;
now=calcStack.top();
calcStack.pop();
calcStack.push(-now);
} else {
double d1,d2,next;
if(calcStack.empty())return false;
d2=calcStack.top();
calcStack.pop();
if(calcStack.empty())return false;
d1=calcStack.top();
calcStack.pop();
switch(reversePorland[i].opType) {
case OPERATOR_ADD: next=d1+d2;break;
case OPERATOR_SUB: next=d1-d2;break;
case OPERATOR_MUL: next=d1*d2;break;
case OPERATOR_DIV:
if(d2==0)return false;
next=d1/d2;break;
case OPERATOR_POW: next=pow(d1,d2);break;
default: return false;
}
calcStack.push(next);
}
}
if(calcStack.empty())return false;
result=calcStack.top();
calcStack.pop();
if(!calcStack.empty())return false;
return true;
}
//テストコード
int main(void) {
string query;
double result;
while(getline(cin,query)) {
if(strcalc(result,query))printf("%f\n",result); else puts("error");
}
return 0;
}