秋月琥珀

逻辑式计算器(C语言)

离散作业真的难顶QAQ,非得整个编程题
输入一个逻辑式,计算变元的所有可能,判断逻辑式是否是可满足的
例如

输入
!((PdQ)cP)#

输出

====================================================================
        输入规则为c=Λ,d=V,i=→,e=←→,!=┐
        命题变元请用大写字母表示
        支持26个字母变元
        逻辑式请尽可能用括号表示运算顺序
        并以#结尾!
====================================================================
请输入表达式(以#结尾):!((PdQ)cP)#
输入的表达式为:
┐((PVQ)ΛP)

┐((0V0)Λ0)     的计算结果是:  1
┐((1V0)Λ1)     的计算结果是:  0
┐((0V1)Λ0)     的计算结果是:  1
┐((1V1)Λ1)     的计算结果是:  0

此命题是可满足的

请按任意键继续. . .

整体代码是在数值表达式计算器的基础上改进的
也是通过优先级表来实现优先级判断,其中的比较符也可以用数值表示优先级
基本的注释咱应该都写上了,实际也测试过了
运行环境是visual studio 2019

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>

#define INIT_STACK_SIZE 100    //栈最大长度
#define SET_NUM 8          //运算符数量

//字符优先级表
unsigned char prior[SET_NUM][SET_NUM] =
{
    /*        'Λ' 'V'  '→''←→''(' ')'  '#'  '!' */
    /*'Λ'*/  '>', '>', '>', '>', '<','>', '>', '<',
    /*'V '*/  '>', '>', '>', '>', '<','>', '>', '<',
    /*'→'*/  '>', '>', '>', '>', '<','>', '>', '<',
    /*'←→'*/'>', '>', '>', '>', '<','>', '>', '<',
    /*'('*/   '<', '<', '<', '<', '<','=', ' ', '<',
    /*')'*/   '>', '>', '>', '>', ' ','>', '>', '>',
    /*'#'*/   '<', '<', '<', '<', '<',' ', '=', '<',
    /*'!'*/   '>', '>', '>', '>', '<','>', '>', '>'
};

//表示支持的运算符,定义比较矩阵的行列对应方式
unsigned char priorSet[SET_NUM] = { 'c', 'd', 'i', 'e','(', ')', '#', '!' };

//结构体定义,这是用来存放字符的栈
typedef struct
{
    char* base;
    char* top;
    int stacksize;
} SqStackC;

//结构体定义,这是用来存放数字的栈
typedef struct
{
    int* base;
    int* top;
    int stacksize;
} SqStackN;

//函数定义
void initStackN(SqStackN&);
void initStackC(SqStackC&);
void pushN(SqStackN&, int);
void pushC(SqStackN&, int);
void popN(SqStackN&, int&);
void popC(SqStackN&, int&);
int calculate(int, char, int, SqStackN&);
int findInSet(char);
char compare(char, char);
void getSolution();

//初始化数字栈

void initStackN(SqStackN& S)
{
    S.base = (int*)malloc(INIT_STACK_SIZE * sizeof(int));
    S.top = S.base;
    S.stacksize = INIT_STACK_SIZE;
}

//初始化字符栈

void initStackC(SqStackC& S)
{
    S.base = (char*)malloc(INIT_STACK_SIZE * sizeof(char));
    S.top = S.base;
    S.stacksize = INIT_STACK_SIZE;
}

//向数字栈中存放数字

void pushN(SqStackN& S, int x)
{
    if (S.top - S.base >= S.stacksize)
        return;
    *(S.top++) = x;
}

//向字符栈中存放字符
void pushC(SqStackC& S, char x)
{
    if (S.top - S.base >= S.stacksize)
        return;
    *(S.top++) = x;
}

//从数字栈中取出数字
void popN(SqStackN& S, int& x)
{
    if (S.top == S.base)
        return;
    x = *(--S.top);
}

//从字符栈中取出字符
void popC(SqStackC& S, char& x)
{
    if (S.top == S.base)
        return;
    x = *(--S.top);
}

//这个函数返回aoperation b的值。假如operation为'+',则返回a+b的值
int calculate(int a, char operation, int b, SqStackN& S)
{
    int o = 1;
    /*判断operation,返回对应的计算结果*/
    switch (operation) {
    case 'c':
        return a & b;
    case 'd':
        return a | b;
    case 'i':
        if (a == 1 && b == 0)o = 0;
        return o;
    case 'e':
        return a == b;
    default:
        return 0;
    }
}

//查找字符c在priorSet中的什么位置,priorSet是所支持的所有字符的集合
int findInSet(char c)
{
    int i;
    for (i = 0; i < SET_NUM; i++) {
        if (priorSet[i] == c)
            return i;
    }
    return -1;
}

//比较optrtop和c的优先关系
char compare(char optrtop, char c)
{
    int i, j;
    //从priorSet中取出optrtop和c的位置
    i = findInSet(optrtop);
    j = findInSet(c);
    //如果返回值中有-1表示这个符号不支持,结束程序
    if (i == -1 || j == -1) {
        printf("不支持的符号类型\n");
        exit(0);
    }
    //否则返回二者优先级关系
    else
        return prior[i][j];
}

void getSolution()
{
    SqStackC Optr;
    SqStackN Num;
    //获得表达式
    printf("====================================================================");
    printf("\n\t输入规则为c=Λ,d=V,i=→,e=←→,!=┐\n\t命题变元请用大写字母表示\n\t支持26个字母变元\n\t逻辑式请尽可能用括号表示运算顺序\n\t并以#结尾!\n");
    printf("====================================================================\n");
    printf("请输入表达式(以#结尾):");
    char input[100] = { 0 };
    char in[100] = { 0 };
    int a[26] = { 0 };
    int b[26] = { 0 };
    int numofobject = 0;
    int iftrue = 0;
    scanf_s("%s", in, 100);
    printf("输入的表达式为:\n");
    //将表达式转化为正常的表示方式,此段可省略
    for (int i = 0; in[i] != '#'; i++) {
        if (in[i] == '!')printf("┐");
        else if (in[i] == 'c')printf("Λ");
        else if (in[i] == 'd')printf("V");
        else if (in[i] == 'i')printf("→");
        else if (in[i] == 'i')printf("←→");
        else printf("%c", in[i]);
    }
    printf("\n\n");

    //处理变元,得到变元的数量和变元的名称,名称以ascii码存在b中,使用a[b[i]]来获取变元的值
    for (int i = 0; in[i] != '#'; i++) {
        if (in[i] >= 65 && in[i] <= 90) {
            if (a[in[i] - 65] == 0) {
                a[in[i] - 65] = 1;
                b[numofobject] = in[i] - 65;
                numofobject++;
            }
        }
    }

    //主要处理部分,n个变元有2^n种可能情况
    for (int times = 0; times < pow(2, numofobject); times++) {

        //通过位运算对每个变元进行取值,比如三个变元取101时,此时times=5,每次位运算得到一位,则a[b[0]]=1,a[b[1]]=0,a[b[2]]=1
        //变元变化顺序取决于在字母表中的位置,靠前的为低位
        for (int f = 0; f < numofobject; f++) {
            a[b[f]] = ((times >> f) & 1)+48;

        }

        //将这种情况生成为代入了数值的表达式,并在最后添上#
        int end = 0;
        for (int i = 0; in[i] != '#'; i++) {
            if (in[i] >= 65 && in[i] <= 90) {
                input[i] = a[in[i] - 65];
            }
            else {
                input[i] = in[i];
            }
            end = i;
        }
        input[end + 1] = '#';

        //表达式输出部分,显示代入后的表达式,可省略
        for (int i = 0; input[i] != '#'; i++) {
            if (input[i] == '!')printf("┐");
            else if (input[i] == 'c')printf("Λ");
            else if (input[i] == 'd')printf("V");
            else if (input[i] == 'i')printf("→");
            else if (input[i] == 'i')printf("←→");
            else printf("%c", input[i]);
        }

        //初始化栈,并在运算符栈放入终止符以便比较
        initStackC(Optr);
        initStackN(Num);
        pushC(Optr, '#');

        //计算模块
        for (int i = 0; input[i] != '#'; i++) {

            //遇到数字则入栈
            if ('0' == input[i] || input[i] == '1') {
                int x = input[i]-48;
                pushN(Num, x);
            }

            //处理操作符
            if (input[i] == 'c' || input[i] == 'd' || input[i] == 'i' || input[i] == 'e' || input[i] == '(' || input[i] == ')' || input[i] == '!') {
            a:          
                char topstack;              //获取栈顶元素
                popC(Optr, topstack);
                pushC(Optr, topstack);
                char cmp = compare(topstack, input[i]);
                if (cmp == '>') {
                    //单目操作符单独运算
                    if (topstack == '!') {
                        int op;
                        popN(Num, op);
                        op = !op;
                        popC(Optr, topstack);
                        pushN(Num, op);
                    }
                    else {
                        int op2, op1;
                        popN(Num, op2); popN(Num, op1);
                        int out = calculate(op1, topstack, op2, Num);
                        popC(Optr, topstack);
                        pushN(Num, out);
                    }
                    goto a;
                }
                if (cmp == '=') {                   //匹配括号
                    popC(Optr, topstack);
                }
                if (cmp == '<') {
                    pushC(Optr, input[i]);
                }

            }
        }

        //计算剩余操作符
        while (Optr.top != Optr.base + 1) {         
            int op2, op1;
            char topstack;
            int out;
            popC(Optr, topstack);
            if (topstack == '!') {
                int op;
                popN(Num, op);
                out = !op;
            }
            else {
                popN(Num, op2); popN(Num, op1);
                out = calculate(op1, topstack, op2, Num);
            }
            pushN(Num, out);
        }

        //数字栈的最后一项就是运算结果
        int output;
        popN(Num, output);
        if (output == 1) {
            iftrue = 1;
        }
        printf("\t的计算结果是:\t%d\n", output);
    }

    //判断命题是否可能满足
    if (iftrue == 1) {
        printf("\n此命题是可满足的\n\n");
    }
    else {
        printf("\n此命题是不可满足的\n\n");
    }
}

//主函数部分
int main()
{
    getSolution();
    system("pause");
    return 0;
}