c++实现简单eval

题目链接: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;
}
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值