C++实现逻辑表达式的真值表建立与等价判定

逻辑表达式真值表的建立与等价判断

首先设计真值表的ADT:

class trueTable{
    vector<string> logicElement;    //组成元素
    vector<vector<int>> table;      //真值表
    int basicNum;
    int column;

    public:
    bool cmp(trueTable T){
        int k1=this->logicElement.size(),k2=T.logicElement.size();
        if(T.column!=this->column) return 0;
        int n1=this->basicNum,n2=T.basicNum;
        if(n1!=n2) return 0;  
        for(int i=0;i<n1;i++){  //基本元素不同
            if(this->logicElement[i]!=T.logicElement[i]){
                return 0;
            }
        }

        for(int i=0;i<column;i++){
            if(this->table[i][k1-1]!=T.table[i][k2-1]) return 0;
        }
        return 1;

    }
    void cal_not(string s){     //!
        logicElement.push_back("!"+s);  //推入新元素名称
        int n=search(s);
        for(int i=0;i<column;i++){  //做非运算
            table[i].push_back(1-table[i][n]);
        }
        return;
    }

    int And(int a,int b){   //与运算
        if(a==1 && b==1) return 1;
        return 0;
    }
    void cal_and(string s1,string s2){  //&
        logicElement.push_back(s1+"&"+s2);
        int n1=search(s1),n2=search(s2);
        for(int i=0;i<column;i++){
            table[i].push_back(And(table[i][n1],table[i][n2]));
        }
        return ;
    }

    int Or(int a,int b){
        return !(a==0 && b==0); 
    }
    void cal_or(string s1,string s2){  //|
        logicElement.push_back(s1+"|"+s2);
        int n1=search(s1),n2=search(s2);
        for(int i=0;i<column;i++){
            table[i].push_back(Or(table[i][n1],table[i][n2]));
        }
        return ;
    }

    int If(int a,int b){
        return !(a==1 && b==0);
    }
    void cal_if(string s1,string s2){   //->
        logicElement.push_back(s1+"->"+s2);
        int n1=search(s1),n2=search(s2);
        for(int i=0;i<column;i++){
            table[i].push_back(If(table[i][n1],table[i][n2]));
        }
        return ;
    }

    bool add(string s){ //添加初始元素
        if(-1==search(s)){
            logicElement.push_back(s);
            return 1;
        }
        return 0;
    }
    int search(string s){   //按名称查找下标,注意排除外括号
        int k=logicElement.size();
        if(s[0]=='('){
            bool f=1;
            for(int i=1;i<k && f;i++){
                if(s[i]==')' && i!=k-1) f=0;  //说明不是外括号
            }
            if(f){    //去掉外括号
                s=s.substr(1,s.size()-2);
            } 
        }
        for(int i=0;i<k;i++){
            if(s==logicElement[i]) return i;
        }
        return -1;
    }

    void reOrder(){     //基本元素重排序
        int k=logicElement.size();
        for(int i=0;i<k-1;i++){
            for(int j=k-1;j>i;j--){
                if(logicElement[j]<logicElement[j-1]){      //冒泡排成字符表序
                    string tmp=logicElement[j];
                    logicElement[j]=logicElement[j-1];
                    logicElement[j-1]=tmp;
                }
            }
        }
        return ;
    }

    void initialLise(){
        int k=logicElement.size();
        this->basicNum=k;
        this->column=1;
        if(k==0) this->column=0;
        else {
            for(int i=0;i<k;i++){
                this->column*=2;
            }
        }
        vector<int> v;
        initialLise_r(v,k);     //初始化基本元素的真值表
        reOrder();
        return ;
    }
    void initialLise_r(vector<int> v,int k){
        vector<int> v1=v;
        if(k==1){
            for(int i=0;i<2;i++){
                v1.push_back(i);
                this->table.push_back(v1);
                v1=v;
            }
            return ;
        } else {
            for(int i=0;i<2;i++){
                v1.push_back(i);
                initialLise_r(v1,k-1);
                v1=v;
            }
            return ;
        }
    }
    void printTable(){
        int k=logicElement.size();
        for(int i=0;i<this->column+1;i++){
            for(int j=0;j<k;j++){
                if(!i){
                    cout<<logicElement[j]<<" ";
                }
                else{
                    cout<<table[i-1][j]<<" ";
                    int len=logicElement[j].size()-1;
                    if(len)
                        for(int p=0;p<len;p++){
                            cout<<" ";
                        }
                }
            }
            cout<<endl;
        }
    }
};

封装了相应的功能,放弃了将命题本身封装为一个类,这样做的好处是直观且易于维护,编写起来效率高,但还不是最好的选择,还能再进行高度封装。

主程序部分:

#include <iostream>
#include <string>
#include <stack>
#include <sstream>
#include <vector>
#include <cmath>
#include <stack>
using namespace std;
char test;

class trueTable{
    vector<string> logicElement;    //组成元素
    vector<vector<int>> table;      //真值表
    int basicNum;
    int column;

    public:
    bool cmp(trueTable T){
        int k1=this->logicElement.size(),k2=T.logicElement.size();
        if(T.column!=this->column) return 0;
        int n1=this->basicNum,n2=T.basicNum;
        if(n1!=n2) return 0;  
        for(int i=0;i<n1;i++){  //基本元素不同
            if(this->logicElement[i]!=T.logicElement[i]){
                return 0;
            }
        }

        for(int i=0;i<column;i++){
            if(this->table[i][k1-1]!=T.table[i][k2-1]) return 0;
        }
        return 1;

    }
    void cal_not(string s){     //!
        logicElement.push_back("!"+s);  //推入新元素名称
        int n=search(s);
        for(int i=0;i<column;i++){  //做非运算
            table[i].push_back(1-table[i][n]);
        }
        return;
    }

    int And(int a,int b){   //与运算
        if(a==1 && b==1) return 1;
        return 0;
    }
    void cal_and(string s1,string s2){  //&
        logicElement.push_back(s1+"&"+s2);
        int n1=search(s1),n2=search(s2);
        for(int i=0;i<column;i++){
            table[i].push_back(And(table[i][n1],table[i][n2]));
        }
        return ;
    }

    int Or(int a,int b){
        return !(a==0 && b==0); 
    }
    void cal_or(string s1,string s2){  //|
        logicElement.push_back(s1+"|"+s2);
        int n1=search(s1),n2=search(s2);
        for(int i=0;i<column;i++){
            table[i].push_back(Or(table[i][n1],table[i][n2]));
        }
        return ;
    }

    int If(int a,int b){
        return !(a==0 && b==1);
    }
    void cal_if(string s1,string s2){   //->
        logicElement.push_back(s1+"->"+s2);
        int n1=search(s1),n2=search(s2);
        for(int i=0;i<column;i++){
            table[i].push_back(If(table[i][n1],table[i][n2]));
        }
        return ;
    }

    bool add(string s){ //添加初始元素
        if(-1==search(s)){
            logicElement.push_back(s);
            return 1;
        }
        return 0;
    }
    int search(string s){   //按名称查找下标,注意排除外括号
        int k=logicElement.size();
        if(s[0]=='('){
            bool f=1;
            for(int i=1;i<k && f;i++){
                if(s[i]==')' && i!=k-1) f=0;  //说明不是外括号
            }
            if(f){    //去掉外括号
                s=s.substr(1,s.size()-2);
            } 
        }
        for(int i=0;i<k;i++){
            if(s==logicElement[i]) return i;
        }
        return -1;
    }

    void reOrder(){     //基本元素重排序
        int k=logicElement.size();
        for(int i=0;i<k-1;i++){
            for(int j=k-1;j>i;j--){
                if(logicElement[j]<logicElement[j-1]){      //冒泡排成字符表序
                    string tmp=logicElement[j];
                    logicElement[j]=logicElement[j-1];
                    logicElement[j-1]=tmp;
                }
            }
        }
        return ;
    }

    void initialLise(){
        int k=logicElement.size();
        this->basicNum=k;
        this->column=1;
        if(k==0) this->column=0;
        else {
            for(int i=0;i<k;i++){
                this->column*=2;
            }
        }
        vector<int> v;
        initialLise_r(v,k);     //初始化基本元素的真值表
        reOrder();
        return ;
    }
    void initialLise_r(vector<int> v,int k){
        vector<int> v1=v;
        if(k==1){
            for(int i=0;i<2;i++){
                v1.push_back(i);
                this->table.push_back(v1);
                v1=v;
            }
            return ;
        } else {
            for(int i=0;i<2;i++){
                v1.push_back(i);
                initialLise_r(v1,k-1);
                v1=v;
            }
            return ;
        }
    }
    void printTable(){
        int k=logicElement.size();
        for(int i=0;i<this->column+1;i++){
            for(int j=0;j<k;j++){
                if(!i){
                    cout<<logicElement[j]<<" ";
                }
                else{
                    cout<<table[i-1][j]<<" ";
                    int len=logicElement[j].size()-1;
                    if(len)
                        for(int p=0;p<len;p++){
                            cout<<" ";
                        }
                }
            }
            cout<<endl;
        }
    }
};

const int opt[7][7]={0,2,2,2,2,2,2,1,1,1,2,2,1,1,1,2,1,2,2,1,1,1,1,1,1,2,1,1,3,2,2,2,2,0,2,1,1,1,1,3,1,1,1,2,2,2,2,1,1};     //运算符优先级,1是大于,0是等于,2是小于,3是error
/*
   # & | ! ( ) ->
#  0 2 2 2 2 2 2
&  1 1 1 2 2 1 1
|  1 2 1 2 2 1 1
!  1 1 1 1 2 1 1
(  3 2 2 2 2 0 2
)  1 1 1 1 3 1 1
-> 1 2 2 2 2 1 1

*/

int searchFor(string s){    //返回符号的数组下标
    if(s=="#") return 0;
    if(s=="&") return 1;
    if(s=="|") return 2;
    if(s=="!") return 3;
    if(s=="(") return 4;
    if(s==")") return 5;
    if(s=="->" || s=="-") return 6;
    return -1;
}

void logicConstruct(string s,trueTable &T){
    stack<string> ele;
    stack<string> op;
    op.push("#");
    s=s+"#";
    int k=s.length();

    for(int i=0;i<k;i++){   //初始化
        switch(s[i]){
            case '#':
            case '&':
            case '|':
            case '!':
            case '-':
            case '>':
            case '(':
            case ')':
            case ' ':break;
            default:{   //添入基本元素
                T.add(s.substr(i,1));
            }
        }
    }
    T.initialLise();

    for(int i=0;i<k;i++){
        if(s[i]==' ') continue;
    
        string tmp=op.top();
        if(searchFor(s.substr(i,1))==-1){
            ele.push(s.substr(i,1));
        } else 
            switch(tmp[0]){
                case '#':{
                    switch(opt[0][searchFor(s.substr(i,1))]){
                        case 0:{
                            op.pop();
                            break;
                        };
                        case 2:{
                            if(s[i]=='-'){
                                op.push(s.substr(i,2));
                                i+=1;
                            } else {
                                op.push(s.substr(i,1));
                            }
                        }
                    }
                    break;
                };
                case '&':{
                    switch(opt[1][searchFor(s.substr(i,1))]){
                        case 1:{
                            op.pop();
                            string str[2];
                            str[1]=ele.top();
                            ele.pop();
                            str[0]=ele.top();
                            ele.pop();
                            T.cal_and(str[0],str[1]);
                            ele.push(str[0]+"&"+str[1]);
                            i=i-1;
                            break;
                        };
                        case 2:{
                            if(s[i]=='-'){
                                op.push(s.substr(i,2));
                                i+=1;
                            } else {
                                op.push(s.substr(i,1));
                            }
                        }
                    }
                    break;
                };
                case '|':{
                    switch(opt[2][searchFor(s.substr(i,1))]){
                        case 1:{
                            op.pop();
                            string str[2];
                            str[1]=ele.top();
                            ele.pop();
                            str[0]=ele.top();
                            ele.pop();
                            T.cal_or(str[0],str[1]);
                            ele.push(str[0]+"|"+str[1]);
                            i=i-1;
                            break;
                        };
                        case 2:{
                            if(s[i]=='-'){
                                op.push(s.substr(i,2));
                                i+=1;
                            } else {
                                op.push(s.substr(i,1));
                            }
                        }
                    }
                    break;
                }
                case '!':{
                    switch(opt[3][searchFor(s.substr(i,1))]){
                        case 1:{
                            op.pop();
                            string str;
                            str=ele.top();
                            ele.pop();
                            T.cal_not(str);
                            ele.push("!"+str);
                            i=i-1;
                            break;
                        };
                        case 2:{
                            if(s[i]=='-'){
                                op.push(s.substr(i,2));
                                i+=1;
                            } else {
                                op.push(s.substr(i,1));
                            }
                        }
                    }
                    break;
                }
                case '-':{
                    switch(opt[6][searchFor(s.substr(i,1))]){
                        case 1:{
                            op.pop();
                            string str[2];
                            str[1]=ele.top();
                            ele.pop();
                            str[0]=ele.top();
                            ele.pop();
                            T.cal_if(str[0],str[1]);
                            ele.push(str[0]+"->"+str[1]);
                            i=i-1;
                            break;
                        };
                        case 2:{
                            if(s[i]=='-'){
                                op.push(s.substr(i,2));
                                i+=1;
                            } else {
                                op.push(s.substr(i,1));
                            }
                        }
                    }
                    break;
                }
                case '(':{
                    switch(opt[4][searchFor(s.substr(i,1))]){
                        case 0:{
                            op.pop();
                            string s=ele.top();
                            ele.pop();
                            ele.push("("+s+")");
                            break;
                        };
                        case 2:{
                            if(s[i]=='-'){
                                op.push(s.substr(i,2));
                                i+=1;
                            } else {
                                op.push(s.substr(i,1));
                            }
                        }
                    }
                    break;
                }
                case ')':{

                    break;
                }
                case ' ':break;
            }
    }
    return;
}

int main(){
    trueTable tableP,tableQ;
    string s;
    cout<<"请输入表达式p:";
    getline(cin,s);
    logicConstruct(s,tableP);
    tableP.printTable();
    cout<<"请输入表达式q:";
    getline(cin,s);
    logicConstruct(s,tableQ);
    tableQ.printTable();
    if(tableP.cmp(tableQ)) cout<<"逻辑等价";
    else cout<<"逻辑不等价";
    return 0;
}

ADT建立的主要思路

首先确定将命题与真值分开存储到两个动态数组中,先扫一遍输入的逻辑表达式,将其中的基本命题添加至真值表中并进行排序,方便后续比较。

再根据基本命题的个数确定真值表的行数 column ,同时利用递归函数代替循环的嵌套初始化真值表,将基本命题的真值表构造出来。利用 initialLise 函数初始化该真值表即可。

然后编写各种逻辑运算的处理函数,处理完毕后将新的组合命题和真值添加至表中,真值只需要在原有数组的某尾直接添加即可,行数不变,列数随着命题数量的增加而增加。

表达式的分析运用到了栈处理运算符与中缀表达式的思路,因此最后一个完成计算的命题一定是输入的复合命题,比较真值表时只需要比较最后一列即可。

处理真值与命题的对应关系运用的是搜索函数,但由于有括号的存在,被存储的命题最外层是没有括号的,这就会导致括号包裹后的命题无法被识别,因此搜索函数需要处理最外层的括号(如果存在的话)。同样运算符的优先级用到了矩阵来表示。

由于条件运算符 -> 的程度为2,C++不能用 switch 处理字符串,因此处理运算符在矩阵的下标时需要额外关照 -> ,为其设置一个额外的判准 - ,同时处理的如果是条件运算符则用于遍历的指针 i 需要跳跃两格,因此要在末尾额外加一。

switch 的处理逻辑不同会导致程序的编写思路不同,如果用于分类栈顶符号,好处是可以避免二次分类判断需要调用的逻辑运算函数,坏处是要额外判断当前读到的符号是否特殊,如占两位的"->" 需要指针额外前进一格,空格需要被忽略,是否非运算符而是命题的名称(这里小偷一个懒默认命题的名称长度为1);若用于分类当前读到的符号 s[i] 则需要二次分类判定调用的逻辑运算函数,比较优先级时由于视角不同设定的判准也会有所改变,如遇到右括号在默认语法合法的情况下(其实是懒得写出错)可以给栈顶命题加上括号(这个很重要,会影响输出命题名称的严谨性),而在前一种情况中是不可能分类出右括号的。

当遇到括号时右括号始终不推入栈,而是一直比较直到碰到左括号消除为止。

运算符的优先级矩阵是非严格等级排列的,而只有一个相对等级,这是因为从左向右运算也是一个潜在的优先级判准,以及界限符的一些特殊性质也有所影响。

string[i] 的写法在C++中会返回一个 char 类型的数据,不能直接用于栈,因此需要用到 string.substr(pos,n) 返回从下标"pos"开始的n个字母组成的子串。同时C++中的类编写没有JS那么方便,键值对的键不是字符串而是变量,要想通过字符串检索下标只能通过STL的 map 或者编写分类函数来实现,不是很方便。

posted @   Festu  阅读(432)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示