课本是高等教育出版社出版的《离散数学及其应用》。
程序会自动分析输入的表达式,并且列出真值表,最后打印出主析取范式和主合取范式,最多支持256 个变元。
主要用到的算法:中缀表达式转后缀表达式、后缀表达式求值还有一个二进制加法模拟。
下面上2 个图,第一个是表达式开头没有非运算的(课本P85 例3.5.5):
第二个不但表达式开头有非运算,而且非运算之后并不是一个数值,而是一个操作符(课本P83 例3.5.4):
下面是代码,如果CSDN 的编辑器弄乱了就将就着看吧,本来代码的缩进和排版都是整洁的。
file:///main.c
* main.c * use MinGW Developer Studio to compile * by iSpeller ([email protected]) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define _BUF_LEN (1<<10) #define _STACK_LEN (_BUF_LEN/2) #define _PROP_LEN (1<<7) #define VOID_NUM (0) /* 不存在的运算数 */ typedef char * string; typedef int bool; #define true (1) #define false (0) typedef int data_t; /* 防止各种老式编译器来大姨妈 */ #ifndef _SIZE_T typedef unsigned int size_t; #endif #ifndef _SSIZE_T typedef int ssize_t; #endif /* 范式类型, 析取, 合取 */ enum paradigm { EXTRACT, CONJUNCT, }; /* 优先级大小 等于, 小于, 大于 */ enum priorities { EG=0, NGE=-1, NLE=1, }; /* 联结词(包括英文圆括号)优先级,全真 */ /* 左括号, 非, 合取, 析取, 蕴含, 右括号, 结束符 */ enum { LEFT=5, NOT=4, AND=3, OR=3, CONT=3, RIGHT=2, END=1, }; /* 4个栈,expr 储存后缀表达式,truth_expr 是expr 的真值解释 * ops 储存联结词, truth 做后缀表达式求值栈,truth 最后存放表达式真值 */ struct stack { data_t data[_STACK_LEN]; ssize_t len; size_t len_max; }expr, truth_expr, ops, truth; string input_buf = NULL; /* 存放输入的表达式 */ struct prop { char p; /* 变元名 */ bool v; /* 真值 */ }; struct table { struct prop data[_PROP_LEN]; ssize_t len; size_t len_max; } prop_table; /* 原子命题变元的列表 */ typedef int truth_item; struct truth_table { truth_item data[_STACK_LEN]; ssize_t len; size_t len_max; } truth_table; /* 真值表 */ #define push(s,a) ((s).data[++(s).len] = (a)) #define pop(s) ((s).data[(s).len--]) #define is_empty(s) ((s).len+1) #define get_top(s) ((s).data[(s).len]) #define init(s,m) (((s).len = -1) || ((s).len_max = (m))) #define STRCMP(a,r,b) (strcmp ((a), (b)) r 0) /* 判断字符是否是原子命题变元 */ bool is_op (char c) { switch (c) { case '|': return OR; break; case '&': return AND; break; case '!': return NOT; break; case '>': return CONT; break; case '(': return LEFT; break; case ')': return RIGHT; break; case '#': return END; break; default : return false; } } /* 判断运算符的优先级 */ enum priorities get_priority (char op1, char op2) { if (is_op (op1) == is_op (op2)) return EG; else if (is_op (op1) < is_op (op2)) return NGE; else return NLE; } /* 进行数据运算 */ data_t do_op (char op, bool num1, bool num2) { bool truth = false; if('!' == op) return num1 ? false : true; switch (op) { case '|': truth = (num2 || num1); break; case '&': truth = (num2 && num1); break; case '>': truth = (num2 && !num1) ? false : true; break; default: fprintf (stderr, "Boy, WHAT Did U Have Done!!??\n"); exit (0); } return truth; } /* 判断识别的命题变元是否已经存在 */ bool prop_find (struct prop item) { ssize_t count = 0; for (count=0; count<prop_table.len+1; ++count) if (item.p == prop_table.data[count].p) return true; return false; } /* 判断变元真值是否设置完毕 * 如果真值全真返回真 */ bool set_truth_end (void) { ssize_t count; for (count=0; count<prop_table.len+1; ++count) if (!prop_table.data[count].v) return false; return true; } /* 输出“你好” */ void start (void) { printf ("输入诸如\"P|(Q&R)>!P\" 的表达式,程序将会列出真值表并求出主范式。\n\n"); printf ("其中:\"|\"表示析取;\"&\"表示合取;\"!\"表示非;\">\"表示蕴含。支持英文圆括号。\n"); printf ("字符串处理不是算法的核心,所以不会处理非法输入\n"); printf ("\t----因此当你输入了非法的表达式,你也会得到一个非法的结果 :D\n"); printf ("exit 指令退出。\n\n"); } /* 要求用户输入表达式和原子命题变元的真值 */ void get_input () { char *loc = input_buf; size_t size = 0; struct prop item; /* 清栈 */ init (expr, _STACK_LEN); init (ops, _STACK_LEN); init (truth_expr, _STACK_LEN); init (truth, _STACK_LEN); init (truth_table, _STACK_LEN); init (prop_table, _PROP_LEN); if (!(input_buf = (char *)malloc (_BUF_LEN))) { perror ("malloc ()"); exit (1); } /* 获取表达式 */ do { printf (" # "); if (!fgets (input_buf, _BUF_LEN-1, stdin)) { perror ("fgets ()"); exit (1); } input_buf[strlen (input_buf)-1] = '#'; /* 结束符号 */ if (STRCMP ("exit#", ==, input_buf)) { printf ("再见 :D\n"); exit (0); } } while (STRCMP ("#", ==, input_buf)); /* 识别原子命题变元和联结词并压入变元列表 */ size = strlen (input_buf); for (loc = input_buf; loc-input_buf < size; ++loc) { if (' ' != *loc) if (!is_op (*loc)) { item.p = *loc; if (!prop_find (item)) push (prop_table, item); /* 现在并不赋真值 */ } } } /* input_buf 中的表达式转换为后缀表达式 */ void make_postfix_expr () { data_t item; ssize_t count; enum priorities level; push (ops, '#'); /* 栈底元素,结束符号,优先级最小 */ for (count=0; count<strlen (input_buf); ++count) { item = input_buf[count]; if (' ' == item) continue; if (!is_op (item)) /* 是操作数则压入表达式栈 */ push (expr, item); else if (')' == item || '#' == item) { /* 去除成对的括号和结束标记'#' */ while ('#' != (item = pop (ops))) push (expr, item); pop (ops); } else { level = get_priority (item, get_top (ops)); /* 通过压入一个不存在的操作数,把单目运算符'!' * 当作双目运算符来处理 */ if ('!' == item) push (expr, VOID_NUM); if (NLE == level) { /* 如果后进运算符高于栈顶元素 */ push (ops, item); /* 压入运算符栈 */ if ('(' == item) /* 如果压入了一个左括号 */ push (ops, '#'); /* 压入运算符栈一个结束标记来保持正确的优先级 */ } else { /* 否则 */ push (expr, pop (ops)); /* 栈顶元素压入表达式栈 */ push (ops, item); /* 后进运算符压入运算符栈 */ } } } free (input_buf); } /* 设置变元的初始真值,全假 */ void init_props_truth (void) { ssize_t count = 0; for (count=0; count<prop_table.len+1; ++count) prop_table.data[count].v = false; for (count=0; count<prop_table.len+1; ++count) printf ("%d ", prop_table.data[count].v); /* 立刻计算一次真值,因为其后的计算不包含全假的情况 */ void find_truth (void); find_truth (); } /* 设置变元的真值,把所有变元当做一个二进制数 * 用二进制加法模拟真值,每次调用函数都会给二进制数加一 */ void set_props_truth (void) { bool carry = false; /* 进位标志 */ ssize_t count, count2; for (count=0, carry=true; carry && (count<prop_table.len+1); ++count) { if (prop_table.data[count].v) { prop_table.data[count].v = (carry ? 0 : 1); if (prop_table.len>0) /* 同时要处理前面的位 */ for (count2=1; count2<count+1; ++count2) prop_table.data[count-count2].v = false; carry = (prop_table.data[count].v ? false : true); } else { prop_table.data[count].v = (carry ? 1 : 0); carry = false; } } for (count=0; count<prop_table.len+1; ++count) printf ("%d ", prop_table.data[count].v); void find_truth (void); find_truth (); } /* 调用函数之时默认prop_table 已经设置了一组有效的真值 * 函数计算在这组真值下整个后缀表达式的真值 */ void find_truth (void) { truth_item item; ssize_t count, count2; data_t data, num1, num2, ans; /* 首先把truth_expr 中的变元全部换成真值 */ truth_expr = expr; for (count=0; count<truth_expr.len+1; ++count) { data = truth_expr.data[count]; if (!is_op (data)) { for (count2=0; count2<prop_table.len+1; ++count2) if (data == prop_table.data[count2].p) { truth_expr.data[count] = prop_table.data[count2].v; break; } } } /* 后缀表达式求值 */ for (count=0; count<truth_expr.len+1; ++count) { data = truth_expr.data[count]; if (!is_op (data)) /* 非运算符 */ push (truth, data); else { /* 是运算符 */ num1 = pop (truth); num2 = pop (truth); ans = do_op (data, num1, num2); push (truth, ans); } } /* 储存真值 */ item = pop (truth); push (truth_table, item); /* 顺便打印真值 */ printf ("\t%d\n", truth_table.data[truth_table.len]); } /* 打印主范式 */ void print_main_paradigm (enum paradigm type) { ssize_t count; bool has_find; if ((EXTRACT!=type) && (CONJUNCT!=type)) exit (0); printf ("主%s范式为 : ", (EXTRACT==type) ? "析取" : "合取"); for (count=0, has_find=false; count<truth_table.len+1; ++count) { if ((EXTRACT==type) ? truth_table.data[count] : !truth_table.data[count]) { has_find = true; printf ("%c%d %s ", (EXTRACT==type) ? 'm' : 'M', count, (EXTRACT==type) ? "∨" : "∧"); } } if (has_find) printf ("\b\b \n"); else printf ("为空"); } /* MAIN */ int main (int argc, char *argv[]) { ssize_t count; start (); while (true) { get_input (); make_postfix_expr (); for (count=0; count<prop_table.len+1; ++count) { printf ("%c ", prop_table.data[count].p); } printf ("\t真值\n\n"); init_props_truth (); while (!set_truth_end ()) set_props_truth (); print_main_paradigm (EXTRACT); /* 主析取范式 */ print_main_paradigm (CONJUNCT); /* 主合取范式 */ } return 0; }