布尔表达式递归解法
先简单描述一下题目吧(简要摘抄!)
描述
您将要生成的程序的目标是评估布尔表达式,如下所示:表达式:(V | V)&F&(F | V)其中V代表True,F代表False。表达式可以包括以下运算符:!不是,&是,| 对于或,也允许在操作分组中使用括号。
输入
表达式的长度是可变的,可以用任意数量的空格分隔;
输入文件中的表达式数量是可变的,每个表达式都以新行显示;
输出
对于每个测试表达式,打印“Expression”,然后打印其序列号“:”以及相应测试表达式的结果值。用换行分隔连续测试表达式的输出。
样例输入
( V | V ) & F & ( F| V)
!V | V & V & !F & (F | V ) & (!F | F | !V & V)
(F&F|V|!V&!F&!(F|F&V))
样例输出
Expression 1: F
Expression 2: V
Expression 3: V
一、思路分析
递归解法,采用和郭炜老师(他的MOOC)四则运算表达式同样的思路。
先给出我采用的布尔表达式递归逻辑吧
定义的递归思路不同,后面的解法也会稍有区别。有兴趣的可以看看其他的递归定义。
表达式的定义
这里定义表达式是由一系列 项 通过 | 运算组成的。
我在搜索资料时,也看到有人是将 & 和 | 运算都放在表达式级别,而表达式直接由因子构成。
bool expression_value()
{
bool result = term_value();
while (1)
{
char op=cin.peek();
if (op == ' ') cin.get();//处理空格
else
{
if (op == '|')
{
cin.get();
bool value = term_value();
result |= value;
}
else break;
}
}
return result;
}
项的定义
这里定义项是由一系列 因子 通过 & 运算组成的。
如果将 & 定义到了表达式里,则没有 项 这一块。
bool term_value()
{
bool result = factor_value();
while (1)
{
char op = cin.peek();
if (op == ' ') cin.get();
else
{
if (op == '&')
{
cin.get();
bool value = factor_value();
result &= value;
}
else break;
}
}
return result;
}
因子的定义
这里定义的因子分三种情况。
(1)逻辑值V或F。这是递归的出口,
(2)括号括起来的表达式。递归调用表达式
(3)取反的因子。递归调用因子自身
有的代码里单独定义了取反运算,我认为这是完全没有必要的。被取反的值一定在我们定义的三种因子类型里,换句话说,被取反的一定是一个因子,我们只需要调用因子自身即可。此外,我们也没必要考虑连续取反的情况了,它是涵盖在了因子的定义里的。
bool factor_value()
{
bool result = 0;
char factor = cin.peek();
while (factor == ' ')
{
cin.get();
factor = cin.peek();
}
switch (factor)
{
case '(': // (表达式)
cin.get();
result = expression_value();
cin.get();
break;
case '!': // !因子
cin.get();
result = !factor_value();
break;
case 'V': // V
cin.get();
result = true;
break;
case 'F': // F
cin.get();
result = false;
break;
default:
break;
}
return result;
}
二、源代码
几乎码了一天,心累
// boolean expression.cpp
//
#include <iostream>
#include <string.h>
using namespace std;
bool expression_value();//表达式
bool term_value(); //项
bool factor_value(); //因子
int main()
{
int i = 1;
while (cin.peek()!=EOF)//文件结束符
{
while (cin.peek() == '\n' || cin.peek() == ' ') //处理无意义的回车或空格
cin.get();
cout<<"Expression "<<i++<<": "<< (expression_value() == true ? "V" : "F") << endl;
}
return 0;
}
bool expression_value()
{
bool result = term_value();
while (1)
{
char op=cin.peek();
if (op == ' ') cin.get();//处理空格
else
{
if (op == '|')
{
cin.get();
bool value = term_value();
result |= value;
}
else break;
}
}
return result;
}
bool term_value()
{
bool result = factor_value();
while (1)
{
char op = cin.peek();
if (op == ' ') cin.get();
else
{
if (op == '&')
{
cin.get();
bool value = factor_value();
result &= value;
}
else break;
}
}
return result;
}
bool factor_value()
{
bool result = 0;
char factor = cin.peek();
while (factor == ' ')
{
cin.get();
factor = cin.peek();
}
switch (factor)
{
case '(': // (表达式)
cin.get();
result = expression_value();
cin.get();
break;
case '!': // !因子
cin.get();
result = !factor_value();
break;
case 'V': // V
cin.get();
result = true;
break;
case 'F': // F
cin.get();
result = false;
break;
default:
break;
}
return result;
}
三、那一天我踩过的坑
只想说,真是太坑了。可在回过头来想,也没发现困难的地方,莫名的坑。
(1)空格要处理
这个其实不算坑,因为题目里说到了,注意一下就没问题了。在表达式、项和因子的处理过程中,都要处理好空格。
(2)文件结束符EOF的应用
大概是我没有读懂题目,之前在主程序里傻傻的用 while(1) 循环。
while (cin.peek()!=EOF)//文件结束符
{
while (cin.peek() == '\n' || cin.peek() == ' ') //处理无意义的回车或空格
cin.get();
cout<<"Expression "<<i++<<": "<< (expression_value() == true ? "V" : "F") << endl;
}
顺便说一下我在内层里写的while循环,个人还是比较满意的。一方面是处理掉每行表达式末尾的 ‘\n’;另一方面,能避免单独的按回车键(或者空格+回车)就开始计算的情况,也就是说空白行会跳过。虽然网页上没有这个case,但这样处理一下个人觉得舒服。
(3)天坑
我的浏览器自动把题目翻译成中文了,我输出了long long time的:
表达式 1 :……
表达式 2 :……
…………
提交一直报错,几近奔溃,被自己蠢哭。
以后得小心,别被自己神坑。
我想获得一个同情的赞!!!!!!!!