要求:程序接受命题公式输入,输出是该公式对应的真值表;
能够处理的连接词至少包括合取、析取、否定、和括号。
使用C语言编程;提交源代码(要求有注释)和可执行文件。
程序代码:
// sg12225028.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <IOSTREAM>
#include <VECTOR>
#include <STACK>
#include <STRING>
using namespace std;
//获取整型数据某一位的值
#define get_bit(datax,bitx) ((((unsigned)datax)>>bitx)&0x01)
void transform(const string &source,string &dest)
//把表达式字符串转换成后项表达式
{
dest="";
bool mark = false;//positive - false ; negtive - true
stack<char> stk_c;//栈 用来临时存放操作符
for(int i=0;i<source.size();i++)
{
char c_tmp = source.at(i);
if(c_tmp>='a'&&c_tmp<='z')
{
dest.append(1,c_tmp);
if(mark) //添加负号
{//添加取反操作进入目标字符串 恢复标志
dest.append(1,'~');
mark = false;
}
continue;
}
if(c_tmp == '~')
{ //遇取反符号 设置标志
mark = true;
continue;
}
if(c_tmp=='(')
{//遇左括号 压栈 临时存储
stk_c.push(c_tmp);
continue;
}
if(c_tmp == ')')
{//遇右括号 弹出栈中左括号以后入栈内容
while(!stk_c.empty() )
{
char c1 = stk_c.top();
stk_c.pop();
if(c1 == '(')
break;
dest.append(1,c1);
}
continue ;
}
if(c_tmp == '*')
{//遇2元操作符 弹出栈中高优先级的操作符 并将当前操作符压栈
char c2;
while(!stk_c.empty() )
{
c2 = stk_c.top();
if(c2 == '+' || c2 == '(')
break;
stk_c.pop();
dest.append(1,c2);
}
stk_c.push(c_tmp);
continue ;
}
if (c_tmp == '+')
{//遇2元操作符 弹出栈中高优先级的操作符 并将当前操作符压栈
char c3;
while(!stk_c.empty() )
{
c3 = stk_c.top();
if(c3 == '(')
break;
stk_c.pop();
dest.append(1,c3);
}
stk_c.push(c_tmp);
continue ;
}
}
while(!stk_c.empty() )
{//把最后留在栈中的操作符弹出 追加到目标字符串尾
dest.append(1,stk_c.top());
stk_c.pop();
}
}
int get_num_vars(string &expression)
{//获取字符串中具有不同ascii的字符个数
int num = 0;
for(char a ='a';a<='z';a++)
{
if(expression.find(a,0)!=string::npos)
num ++;
}
return num;
}
void resolve(string &expression)
{//根据后项表达式求解真值表
//var_num 表达式中所使用的字符个数
int var_num = get_num_vars(expression);
//定义一张表 用来存储'a'-'z'在KEY的各个位上的映射
//-1表示无映射 即未使用此变量
//表中多出的一项用来存储表达式的临时结果 由于求解表达式需要多次循环迭代
//故有此用法 方便求解
int table_alpha[27];
for(int tmp=0;tmp<27;tmp++)
table_alpha[tmp]=-1;
//最后的表项留给结果使用 赋值26可使寻址前后值不变
table_alpha[26]=26;
//offset用于在KEY的各个位之间寻址 确定各变量的值
int offset = 0;
//KEY的每一位表示一个变量 下述for循环可遍历所有变量的0 1组合情况
for(unsigned int KEY = 0; KEY<(1<<var_num) ;KEY++)
{
string str=expression ;
bool result = false;
stack<char> oprands;//栈 用来临时存放操作数 即变量字符
//取反标志 false 代表不取反;true代表取反
bool mark[27];
for(int tmp1=0;tmp1<27;tmp1++)
mark[tmp1] = false;
for(;!str.empty();str = str.substr(1))
{//每次处理后 截断字符串 只保留未处理部分
if(str.at(0)>='a'&&str.at(0)<='z')
{//取操作数
int op = str.at(0);
//如果当前字符未分配过KEY的指定位,那么现在分配一个空余位
//offset就是当前字符所分配到位 它总是正的
//从低位到高位分配 当一个位被分配后 offset就移向下一个位
if(table_alpha[op-'a']==-1)
table_alpha[op-'a'] = offset++;
//操作数压栈
oprands.push(op);
continue ;
}
//遇~号先保存相应变量的取反标志,后续在具体求取2元运算时处理
if(str.at(0)=='~')
{
int id = oprands.top()-'a';
mark[id]= !mark[id];
continue ;
}
//遇到+或*操作,倒空操作数栈 求取表达式的值
if(str.at(0)=='+' || str.at(0)=='*')
{
int oprand1,oprand2;
int mark1,mark2;
//oprand1 oprand2分别存储操作数(字符)相对于'a'的偏移量
//方便在table_alpha mark 两张表中查询数据
oprand1 = oprands.top() - 'a'; oprands.pop();
oprand2 = oprands.top() - 'a'; oprands.pop();
//mark1 mark2分别表示两个操作数的取反标志
mark1 = mark[oprand1];
mark2 = mark[oprand2];
//oprand1 oprand2分别表示两个操作数的偏移量
oprand1 = table_alpha[oprand1];
oprand2 = table_alpha[oprand2];
if(oprand1<26)//正常操作数 获取KEY响应位上的值
oprand1 = mark1 ?(!get_bit(KEY,oprand1)): get_bit(KEY,oprand1) ;
else if (oprand1==26)//如果是上次的结果
oprand1 = result;
if(oprand2<26)//正常操作数 获取KEY响应位上的值
oprand2 = mark2 ?(!get_bit(KEY,oprand2)): get_bit(KEY,oprand2) ;
else if (oprand2==26)//如果是上次的结果
oprand2 = result;
//计算结果
if(str.at(0)=='+')
result = oprand1 || oprand2 ;
else
result = oprand1 && oprand2 ;
//将结果压栈 此处只需要压入结果相对于'a'的偏移量即26即可
oprands.push('a'+26);
continue ;
}
}
//已处理完一次循环
for(int mm1 = 25;mm1 >=0;mm1 --)
{
if(table_alpha[mm1]!=-1)
cout << get_bit(KEY,table_alpha[mm1])<< "\t"<<ends;
}
cout << result <<endl;
}
for(int mm0 = 25;mm0 >=0;mm0 --)
{
if(table_alpha[mm0]!=-1)
cout << (char)(mm0+'a')<< "\t"<<ends;
}
cout <<"R"<<endl;
}
int main(int argc, char* argv[])
{
string exp,str2;
cout << "输入一个表达式类似于: a+b*~c*(d+e):"<<endl;
cin >>exp ;
transform(exp,str2);
cout<<"翻译成逆向表达式后:\n"<<str2 <<endl;
cout <<"真值表如下:"<<endl;
resolve(str2);
return 0;
}