小学期2——展示真值表C++版本

酶和ATP 2022年05月21日 643次浏览

521,冤种还在写题🥹
又闻线下上课,但大学牲仍禁止出校,悲伤超级加倍

大概思路是:
先用户输入的中缀表达式先预处理转换成后缀表达式,再用二进制枚举的方法枚举出真值表的每一条。
对于真值的每一条:用每个变量的具体的真值情况(0或1)来替换预处理过后缀表达式中具体的变量,最后再计算输出后缀表达式的真值。

#include <cstring>
#include <iostream>
#include <map>
#include <stack>
#include <vector>
using namespace std;
// 优先级
map<char, int> prior = {{'!', 5}, {'&', 4}, {'|', 3}};
// 是否为运算操作符
bool isOperator(char op) {
    if (op == '|' || op == '&' || op == '!') return true;
    return false;
}
// 中缀转后缀
string getPostfix(string infix) {
    // 一个栈存储运算符
    stack<char> s;
    // 存储后缀表达式
    string postfix;
    for (int i = 0; i < infix.size(); i++) {
        char tmp = infix[i];
        if (isOperator(tmp)) {
            while (!s.empty() && isOperator(s.top()) &&
                   prior[s.top()] >= prior[tmp]) {
                postfix.push_back(s.top());
                s.pop();
            }
            s.push(tmp);
        } else if (tmp == '(') {
            s.push(tmp);
        } else if (tmp == ')') {
            while (s.top() != '(') {
                postfix.push_back(s.top());
                s.pop();
            }
            s.pop();
        } else if (isalpha(tmp))
            postfix.push_back(tmp);
    }
    while (!s.empty()) {
        postfix.push_back(s.top());
        s.pop();
    }
    return postfix;
}
// 计算后缀表达式
bool Caculate(string postfix) {
    // 栈存储真值
    stack<int> s;
    bool left, right;
    bool flag;
    // 从左到右遍历后缀表达式
    for (int i = 0; i < postfix.size(); i++) {
        char c = postfix[i];
        // 如果是真值
        if (c == '0' || c == '1') {
            s.push(c - '0');
            continue;
            // 对 ! 特判,是为了简化代码
        } else if (c == '!') {
            flag = s.top();
            s.pop();
            s.push(!flag);
            continue;
        }
        // 对 | 和 & 的判断
        right = s.top();
        s.pop();
        left = s.top();
        s.pop();
        if (c == '|') {
            s.push(left | right);
            continue;
        } else if (c == '&') {
            s.push(left & right);
            continue;
        }
    }
    return s.top();
}

//真值表输出
void Print(string infix) {
    // 生成后缀表达式 postfix
    string postfix = getPostfix(infix);
    // 存储命题变量
    vector<char> v;
    for (int i = 0; i < postfix.size(); i++)
        if (isalpha(postfix[i])) v.push_back(postfix[i]);
    // 删除重复的命题变量,此时 v 中存储的是命题变量的集合
    // v.size()为命题变量的个数
    v.erase(unique(v.begin(), v.end()), v.end());

    // 输出最上面一行
    // 先输出变量
    for (int i = 0; i < v.size(); i++) printf("%5c", v[i]);
    // 再输出中缀式,
    printf("    %s\n", infix.c_str());

    // 变量有几个,就有 2^n 个表达式
    // 比如有 2 个变量,它的真值情况就是 00, 01, 10, 11 四种情况
    // 转换为十进制就是 0 1 2 3
    // 此时每一个变量的真值情况可以表示为一个二进制位
    for (int i = 0; i < (1 << v.size()); i++) {
        // 将后缀式赋给临时字符串,用于计算
        string tmp = postfix;
        // 输出这一个的变量表达式
        for (int k = 0; k < v.size(); k++) printf("%5d", 1 & (i >> k));
        int j = 0;
        // 将临时字符串中的变量替换为对应的值
        while (j < tmp.size()) {
            if (isalpha(tmp[j])) {
                // 先找到位置
                int index = find(v.begin(), v.end(), tmp[j]) - v.begin();
                // 再找到对应的真值
                tmp[j] = (char)((int)(1 & (i >> index)) + '0');
            }
            j++;
        }
        // 最后输出它的真值
        printf("%5d\n", Caculate(tmp));
    }
    return;
}

int main() {
    string infix;
    printf("请您输入合法表达式(&表示合取,|表示析取,!表示非)\n");
    while (cin >> infix) {
        Print(infix);
        printf("请您输入合法表达式(&表示合取,|表示析取,!表示非)\n");
    }
    return 0;
}