c++实现简单eval

来源:互联网 发布:河北网络干部学院 编辑:程序博客网 时间:2024/05/29 19:26

题目链接:2017 计蒜之道 初赛 第二场——百度的科学计算器(简单)

给定一个合法的字符串(可以含有整数、小数、加号、减号、括号)的字符串,计算其值

// 将s分隔成多个整体(整数、小数、运算符、括号中的部分均算作一个整体)std::vector<std::string> split(const std::string& s) {    std::vector<std::string> ret;    if (s.length() == 0) return ret;    // bracket    if (s.at(0) == '(') {        int cnt = 0;        for (int i = 0; i < s.length(); i++) {            if (s.at(i) == '(') cnt++;            else if (s.at(i) == ')') cnt--;            if (cnt == 0) {                ret.push_back(s.substr(0, i + 1));                auto r = split(s.substr(i + 1));                std::copy(r.begin(), r.end(), std::back_inserter(ret));                break;            }        }    }    // number    else if (('0' <= s.at(0) && s.at(0) <= '9') || s.at(0) == '.') {        ret.push_back("");        for (int i = 0; i < s.length(); i++) {            if (('0' <= s.at(i) && s.at(i) <= '9') || s.at(i) == '.')                ret.back() += s.at(i);            else {                auto r = split(s.substr(i));                std::copy(r.begin(), r.end(), std::back_inserter(ret));                break;            }        }    }    // operator    else {        ret.push_back(s.substr(0, 1));        auto r = split(s.substr(1));        std::copy(r.begin(), r.end(), std::back_inserter(ret));    }    return ret;}// 支持s中含有括号、小数、整数、加号、减号double calc(std::string s) {    auto parts = split(s);    if (parts.size() == 0)         return 0;    if (parts.size() == 1) {        // 如果给定字符串是括号括起来的表达式        if (s.at(0) == '(')             return calc(s.substr(1, s.length() - 2));        else {            assert(('0' <= s.at(0) && s.at(0) <= '9') || s.at(0) == '.');            // stod函数是c++11标准支持的,需要编译器不能太老            return std::stod(s);        }    }    double ret = calc(parts.at(0));    for (int i = 1; i < parts.size(); i += 2) {        assert(i + 1 < parts.size());        if (parts.at(i) == "+")            ret += calc(parts.at(i + 1));        else if (parts.at(i) == "-")            ret -= calc(parts.at(i + 1));        else             assert(false);    }    return ret;}int main(int argc, char *argv[]) {    int n; std::cin >> n;    std::string s;    std::cin >> s;    double ans = calc(s);    if (s.find(".") == std::string::npos)        std::cout << (long long)ans << std::endl;    else        printf("%.6f\n", ans);    return 0;}

另一种比较好的写法,修改自这个blog
这段代码主要思路是:建立表达式树

template<class T>class Calc {    static const int MX = 100000;    char op[MX];    int lch[MX], rch[MX], r = 0;    T s[MX];    bool DIV_ZERO;    int _build(const std::string& S, int L, int R) {        int u;        int t = L;        while (t <= R && (isdigit(S.at(t)) || S.at(t) == '.')) t++;        if (t == R + 1) {            u = r++;            op[u] = '.';            lch[u] = rch[u] = -1;            s[u] = std::stod(S.substr(L, t - L));            return u;        }        std::vector<int> c = { -1, -1 };        for (int i = L, p = 0; i <= R; i++) {            if (S.at(i) == '(') p++;            else if (S.at(i) == ')') p--;            else if ((S.at(i) == '+' || S.at(i) == '-') && !p) c.at(0) = i;            else if ((S.at(i) == '*' || S.at(i) == '/') && !p) c.at(1) = i;        }        if (c.at(0) < 0)            c.at(0) = c.at(1);        if (c.at(0) < 0)            u = _build(S, L + 1, R - 1);        else {            u = r++;            op[u] = S[c[0]];            lch[u] = _build(S, L, c[0] - 1);            rch[u] = _build(S, c[0] + 1, R);            s[u] = -1;        }        return u;    }    T _calc(int u) {        if (op[u] == '.') return s[u];        T al = _calc(lch[u]), ar = _calc(rch[u]);        switch (op[u]) {        case '+': return al + ar;        case '-': return al - ar;        case '*': return al * ar;        case '/': return (ar == 0 ? DIV_ZERO = 1 : al / ar);        }        assert(false); return al;    }public:    T solve(const std::string& s) {        r = 0;        DIV_ZERO = false;        int u = _build(s, 0, (int)s.length() - 1);        if (!DIV_ZERO)            return _calc(u);        else            /// 运算过程中发生除0            return INF;    }};Calc<long long> int_calc;Calc<long double> dbl_calc;int main() {    int n;    while (std::cin >> n) {        std::string expr;        std::cin >> expr;        if (expr.find(".") == std::string::npos) {            long long ans = int_calc.solve(expr);            printf("%lld\n", ans);        }        else {            long double ans = dbl_calc.solve(expr);            printf("%.6f\n", (double)ans);        }    }    return 0;}