如何用c++写一个简单的计算器程序?

作者手把手教如何用栈求中序表达式的值。先从字符串字面量获取字符流,使用 gcc 扩展,准备两个栈保存操作数和操作符。构建状态机,parse char,eval once 进行相应操作,还用到 calc 方法。最后通过递归函数启动,组合各函数完成计算。

作者:小纸条
链接:https://www.zhihu.com/question/28582706/answer/691444859
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

我手把手教题主写吧。

首先我们需要从字符串字面量中获得一个字符流,我们需要使用 gcc 扩展。

template <char...> struct char_vector { using vec_type = char_vector; };

template<typename t, t... cs>
constexpr char_vector<cs...> operator ""_str() { return {}; }

然后我们需要两个栈来保存操作数和操作符。第一个栈是 int 类型的,第二个栈是 char 类型的。我们把栈写好。

template <char...> struct char_vector { using vec_type = char_vector; };

template <typename> struct pop_char {};

template <char c, char... cs>
struct pop_char<char_vector<c, cs...>> : std::integral_constant<char, c>, char_vector<cs...> {};

template <char, typename> struct push_char {};

template <char c, char... cs>
struct push_char<c, char_vector<cs...>> : char_vector<c, cs...> {};

template <int...> struct int_vector { using vec_type = int_vector; };

template <typename> struct pop_int {};

template <int i, int... is>
struct pop_int<int_vector<i, is...>> : std::integral_constant<int, i>, int_vector<is...> {};

template <int, typename> struct push_int {};

template <int i, int... is>
struct push_int<i, int_vector<is...>> : int_vector<i, is...> {};

好了。接下来我们考虑做一个状态机。每一个状态都是 { 操作数栈,操作符栈,输入流 } 的集合。

template <typename ns, typename os, typename s>
struct state
{
    using num_stack = ns;
    using op_stack = os;
    using stream = s;
};

然后我们就可以 parse char 啦。这部分参照网上,很多资料都会讲如何用两个栈求中序表达式的值。我就不赘述啦。直接看代码吧。

template <int... is, char... cs1, char c, char... cs2>
constexpr auto eval_once(state<int_vector<is...>, char_vector<cs1...>,
    char_vector<c, cs2...>>)
{
    if constexpr (c == ' ')
    {
        return state<int_vector<is...>, char_vector<cs1...>,
            char_vector<cs2...>>{};
    }
    else if constexpr (c >= '0' && c <= '9')
    {
        using merge_type = decltype(merge(char_vector<c, cs2...>{}));
        using ops = typename push_int<merge_type::value, int_vector<is...>>::vec_type;
        return state<ops, char_vector<cs1...>, typename merge_type::type>{};
    }
    else if constexpr (c == '(')
    {
        return state<int_vector<is...>, char_vector<c, cs1...>,
            char_vector<cs2...>>{};
    }
    else if constexpr (c == ')')
    {
        return calc_brk(state<int_vector<is...>, char_vector<cs1...>,
            char_vector<c, cs2...>>{});
    }
    else if constexpr (c == '+' || c == '-' || c == '*' || c == '/')
    {
        if constexpr (sizeof...(cs1) == 0)
            return state<int_vector<is...>, char_vector<c, cs1...>,
            char_vector<cs2...>>{};
        else if constexpr (((pop_char<char_vector<cs1...>>::value == '+' ||
            pop_char<char_vector<cs1...>>::value == '-') &&
            (c == '*' || c == '/')) ||
            pop_char<char_vector<cs1...>>::value == '(')
            return state<int_vector<is...>, char_vector<c, cs1...>,
            char_vector<cs2...>>{};
        else
            return calc_op(state<int_vector<is...>, char_vector<cs1...>,
                char_vector<c, cs2...>>{});
    }
}

这个 eval once 来 parse 当前的字符,并且进行相应的操作。其中 merge 操作是合并字符变成一个数字的。

template <int ret = 0, char c, char... cs>
constexpr auto merge(char_vector<c, cs...>)
{
    if constexpr (c >= '0' && c <= '9')
        return merge<10 * ret + (c - '0')>(char_vector<cs...>{});
    else
        return merge_pair<ret, char_vector<c, cs...>>{};
}

template <int ret = 0>
constexpr auto merge(char_vector<>)
{
    return merge_pair<ret, char_vector<>>{};
}

其中 merge pair 就是 { ret, stream } 的集合。这里的 stream 是合并完之后剩的流。

template <int n, typename t>
struct merge_pair
{
    static const int value = n;
    using type = t;
};

我们的 eval once 还用到了两个 calc 方法。其实就是遇到 ) 和优先级低的操作符的时候要进行的计算。

template <int... is, char... cs1, char c, char... cs2>
constexpr auto calc_brk(state<int_vector<is...>, char_vector<cs1...>,
    char_vector<c, cs2...>>)
{
    if constexpr (pop_char<char_vector<cs1...>>::value == '(')
        return state<int_vector<is...>,
        typename pop_char<char_vector<cs1...>>::vec_type,
        char_vector<cs2...>>{};
    else
        return calc_brk(calc_once<pop_char<char_vector<cs1...>>::value>(
            state<int_vector<is...>, typename pop_char<char_vector<cs1...>>::vec_type,
            char_vector<c, cs2...>>{}));
}

template <int... is, char... cs1, char c, char... cs2>
constexpr auto calc_op(state<int_vector<is...>, char_vector<cs1...>,
    char_vector<c, cs2...>>)
{
    return calc_once<pop_char<char_vector<cs1...>>::value>(
        state<int_vector<is...>,
        typename push_char<c, typename pop_char<char_vector<cs1...>>::vec_type>::vec_type,
        char_vector<cs2...>>{});
}

其中 calc once 就是根据传入的操作符来从操作数栈里弹出两个操作数计算,然后把结果 push 进操作数栈。

template <char op, int i1, int i2, int... is, char... cs1, char... cs2>
constexpr auto calc_once(state<int_vector<i1, i2, is...>, char_vector<cs1...>,
    char_vector<cs2...>>)
{
    if constexpr (op == '+')
        return state<int_vector<i2 + i1, is...>, char_vector<cs1...>,
        char_vector<cs2...>>{};
    else if constexpr (op == '-')
        return state<int_vector<i2 - i1, is...>, char_vector<cs1...>,
        char_vector<cs2...>>{};
    else if constexpr (op == '*')
        return state<int_vector<i2 * i1, is...>, char_vector<cs1...>,
        char_vector<cs2...>>{};
    else if constexpr (op == '/')
        return state<int_vector<i2 / i1, is...>, char_vector<cs1...>,
        char_vector<cs2...>>{};
}

嗯。我们的 eval once 就解释完了。接下来我们需要一个递归函数启动 eval once。

template <int... is, char... cs1, char... cs2>
constexpr auto eval_all(state<int_vector<is...>, char_vector<cs1...>,
    char_vector<cs2...>>)
{
    if constexpr (sizeof...(cs2) == 0)
        return state<int_vector<is...>, char_vector<cs1...>,
        char_vector<cs2...>>{};
    else
        return eval_all(eval_once(state<int_vector<is...>, char_vector<cs1...>,
            char_vector<cs2...>>{}));
}

这个 eval all 函数计算完之后,流就空了。只剩下操作数和操作符两个栈了。我们最后写一个 calc last 函数计算这两个栈的最后结果。

template <int... is, char... cs>
constexpr int calc_last(state<int_vector<is...>, char_vector<cs...>,
    char_vector<>>)
{
    if constexpr (sizeof...(cs) == 0)
        return pop_int<int_vector<is...>>::value;
    else
        return calc_last(calc_once<pop_char<char_vector<cs...>>::value>(
            state<int_vector<is...>,
            typename pop_char<char_vector<cs...>>::vec_type,
            char_vector<>>{}));
}

好啦。最后我们写一个 eval 函数把它们组合起来。

template <typename t>
constexpr int eval(t)
{
    if constexpr (std::is_same_v<t, char_vector<>>)
        return 0;
    else
        return calc_last(eval_all(state<int_vector<>, char_vector<>, t>{}));
}

现在我们可以写这样的代码了。

static_assert(eval("4 * ((3 / 2) + 1) * 6 + 9"_str) == 57, "");

完整代码以及示例的链接:Wandbox

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值