luanma-2333

计算命题演算公式的真值

摘要

【要求】

  • 程序运行有菜单选择。
  • 利用二叉树来计算公式的真值。首先利用堆栈将中缀形式的公式变为后缀
    形式;然后根据后缀形式,从叶结点开始构造相应的二叉树;最后按后序遍历该
    树,求各子树之值,即每到达一个结点,其子树之值已经计算出来,当到达根结
    点时,求得的值就是公式之真值。
  • 逻辑变元的标识符用单字母表示。
  • 打印公式的后序遍历序列。
  • 根据用户的要求,输入各变量的值,计算并显示表达式的真值。
  • 设计多种不同形式的命题演算公式,对各个命题演算公式进行合法性检查。
  • 逻辑变元的标识符不限于单字母,而可以是任意长的字母数字串。
  • 打印创建的二叉树的结构。
  • 根据用户的要求显示公式的真值表。
  • 将命题演算公式中的逻辑运算符增加异或、蕴含、等价。
  • 实现程序的可视化界面。

因为疫情时期闲得慌,而且课设可以用C++,于是打算尽量使用已知的C++特性来实现要求的所有功能,即《C++ primer》书的前十五章,从结果的意义上将数据结构课转化成了C++面向对象编程课,最后写出的代码大概是C with classes + STL。虽然不如人意,但明白这就是自己能力的极限。此次考察之后可能一长段时间内不会再使用C++11,因此作为记载。

可视化版本

图片
可视化版本最终的成品还有很大的问题,集中在实现可视化根据用户的输入来得到表达式的真值的功能上,在获取用户的输入上,因为Qplaintext不能用输入输出流,不能使用std::getline(std::cin,str)。最终用如下代码实现:

	for (auto& i : solver.VariableSet)
	{QString beforInput, value;
		do
		{
			beforInput = text;
			beforInput += QString::fromStdString(std::string("please input the logical value of " + i.first + ":\n"));
			
			text= beforInput;
			ui.plainTextEdit->setPlainText(text);
                        
			ui.plainTextEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
			
                        //休眠来创造能够输入数据的时间
                        QTime t;
			t.start();
			while(t.elapsed()<2000)
			{
				QCoreApplication::processEvents();
			}

			value = text[text.size() - 1];
		} while (text.size()-beforInput.size()!=1||( value != one && value != zero));

		i.second = value.toInt();
	}

首先可以看出用休眠等待的方式给用户输入的时间的方式效果并不好,最好的效果是用户输入并且告知程序输入完毕,再进行while中的检测。其次只有在点击plaintext后才能移动光标到文末,最后在这个过程中如果按重新输入键会这个函数并不会停止,导致了很多问题。

可能的解决方式:

  • Qplaintext的特性。因为VC++垃圾,没认真学MFC(而且工业界MFC早被淘汰了)。第一次用Qt,没有系统的参考资料。曾经有好友送了我一本Qt开发的书,可惜放学校了,疫情时期也无法取得。英文太菜,看不懂官方文档,每遇到一个问题都要打开很多网页去查询,最终才勉强做成。或许Qplaintext中有函数或特性能够解决问题。
  • 线程。让这个线程等待直到获取到输入,曾经这么写过
while(textchange())
QCoreApplication::processEvents();

但程序陷入死循环。个人觉得使用线程应该是最好的解决方式,可惜对此一无所知。

  • 重写。给输入按钮多一个功能,点击完成所有逻辑值的输入。

以下是所有代码,望读者多指摘。

ui类

使用Qt设计师自动生成的

/********************************************************************************
** Form generated from reading UI file \'LogicalExpression.ui\'
**
** Created by: Qt User Interface Compiler version 5.9.9
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_LOGICALEXPRESSION_H
#define UI_LOGICALEXPRESSION_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPlainTextEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QToolBar>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_LogicalExpressionClass
{
public:
    QAction *actionintroduction;
    QWidget *centralWidget;
    QGridLayout *gridLayout_5;
    QPlainTextEdit *plainTextEdit;
    QSpacerItem *horizontalSpacer_2;
    QSpacerItem *horizontalSpacer;
    QPushButton *pushButton_N;
    QPushButton *pushButton_left;
    QPushButton *pushButton_equ;
    QPushButton *pushButton_right;
    QPushButton *pushButton_imply;
    QPushButton *pushButton_input;
    QPushButton *pushButton_xor;
    QPushButton *pushButton_or;
    QPushButton *pushButton_profixTree;
    QPushButton *pushButton_profix;
    QPushButton *pushButton_table;
    QPushButton *pushButton_cal;
    QPushButton *pushButton_and;
    QToolBar *mainToolBar;
    QStatusBar *statusBar;
    QMenuBar *menuBar;
    QMenu *menu;

    void setupUi(QMainWindow *LogicalExpressionClass)
    {
        if (LogicalExpressionClass->objectName().isEmpty())
            LogicalExpressionClass->setObjectName(QStringLiteral("LogicalExpressionClass"));
        LogicalExpressionClass->resize(415, 473);
        actionintroduction = new QAction(LogicalExpressionClass);
        actionintroduction->setObjectName(QStringLiteral("actionintroduction"));
        centralWidget = new QWidget(LogicalExpressionClass);
        centralWidget->setObjectName(QStringLiteral("centralWidget"));
        gridLayout_5 = new QGridLayout(centralWidget);
        gridLayout_5->setSpacing(6);
        gridLayout_5->setContentsMargins(11, 11, 11, 11);
        gridLayout_5->setObjectName(QStringLiteral("gridLayout_5"));
        plainTextEdit = new QPlainTextEdit(centralWidget);
        plainTextEdit->setObjectName(QStringLiteral("plainTextEdit"));
        plainTextEdit->setEnabled(true);
        QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        sizePolicy.setHeightForWidth(plainTextEdit->sizePolicy().hasHeightForWidth());
        plainTextEdit->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(plainTextEdit, 2, 0, 1, 4);

        horizontalSpacer_2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        gridLayout_5->addItem(horizontalSpacer_2, 1, 0, 1, 4);

        horizontalSpacer = new QSpacerItem(575, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);

        gridLayout_5->addItem(horizontalSpacer, 3, 0, 1, 4);

        pushButton_N = new QPushButton(centralWidget);
        pushButton_N->setObjectName(QStringLiteral("pushButton_N"));
        sizePolicy.setHeightForWidth(pushButton_N->sizePolicy().hasHeightForWidth());
        pushButton_N->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_N, 4, 2, 1, 1);

        pushButton_left = new QPushButton(centralWidget);
        pushButton_left->setObjectName(QStringLiteral("pushButton_left"));
        sizePolicy.setHeightForWidth(pushButton_left->sizePolicy().hasHeightForWidth());
        pushButton_left->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_left, 4, 3, 1, 1);

        pushButton_equ = new QPushButton(centralWidget);
        pushButton_equ->setObjectName(QStringLiteral("pushButton_equ"));
        sizePolicy.setHeightForWidth(pushButton_equ->sizePolicy().hasHeightForWidth());
        pushButton_equ->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_equ, 5, 1, 1, 1);

        pushButton_right = new QPushButton(centralWidget);
        pushButton_right->setObjectName(QStringLiteral("pushButton_right"));
        sizePolicy.setHeightForWidth(pushButton_right->sizePolicy().hasHeightForWidth());
        pushButton_right->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_right, 5, 3, 1, 1);

        pushButton_imply = new QPushButton(centralWidget);
        pushButton_imply->setObjectName(QStringLiteral("pushButton_imply"));
        sizePolicy.setHeightForWidth(pushButton_imply->sizePolicy().hasHeightForWidth());
        pushButton_imply->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_imply, 5, 0, 1, 1);

        pushButton_input = new QPushButton(centralWidget);
        pushButton_input->setObjectName(QStringLiteral("pushButton_input"));
        sizePolicy.setHeightForWidth(pushButton_input->sizePolicy().hasHeightForWidth());
        pushButton_input->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_input, 6, 0, 1, 4);

        pushButton_xor = new QPushButton(centralWidget);
        pushButton_xor->setObjectName(QStringLiteral("pushButton_xor"));
        sizePolicy.setHeightForWidth(pushButton_xor->sizePolicy().hasHeightForWidth());
        pushButton_xor->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_xor, 5, 2, 1, 1);

        pushButton_or = new QPushButton(centralWidget);
        pushButton_or->setObjectName(QStringLiteral("pushButton_or"));
        sizePolicy.setHeightForWidth(pushButton_or->sizePolicy().hasHeightForWidth());
        pushButton_or->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_or, 4, 1, 1, 1);

        pushButton_profixTree = new QPushButton(centralWidget);
        pushButton_profixTree->setObjectName(QStringLiteral("pushButton_profixTree"));
        sizePolicy.setHeightForWidth(pushButton_profixTree->sizePolicy().hasHeightForWidth());
        pushButton_profixTree->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_profixTree, 0, 1, 1, 1);

        pushButton_profix = new QPushButton(centralWidget);
        pushButton_profix->setObjectName(QStringLiteral("pushButton_profix"));
        sizePolicy.setHeightForWidth(pushButton_profix->sizePolicy().hasHeightForWidth());
        pushButton_profix->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_profix, 0, 0, 1, 1);

        pushButton_table = new QPushButton(centralWidget);
        pushButton_table->setObjectName(QStringLiteral("pushButton_table"));
        sizePolicy.setHeightForWidth(pushButton_table->sizePolicy().hasHeightForWidth());
        pushButton_table->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_table, 0, 2, 1, 1);

        pushButton_cal = new QPushButton(centralWidget);
        pushButton_cal->setObjectName(QStringLiteral("pushButton_cal"));
        sizePolicy.setHeightForWidth(pushButton_cal->sizePolicy().hasHeightForWidth());
        pushButton_cal->setSizePolicy(sizePolicy);

        gridLayout_5->addWidget(pushButton_cal, 0, 3, 1, 1);

        pushButton_and = new QPushButton(centralWidget);
        pushButton_and->setObjectName(QStringLiteral("pushButton_and"));
        sizePolicy.setHeightForWidth(pushButton_and->sizePolicy().hasHeightForWidth());
        pushButton_and->setSizePolicy(sizePolicy);
        pushButton_and->setAutoDefault(false);

        gridLayout_5->addWidget(pushButton_and, 4, 0, 1, 1);

        LogicalExpressionClass->setCentralWidget(centralWidget);
        mainToolBar = new QToolBar(LogicalExpressionClass);
        mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
        LogicalExpressionClass->addToolBar(Qt::TopToolBarArea, mainToolBar);
        statusBar = new QStatusBar(LogicalExpressionClass);
        statusBar->setObjectName(QStringLiteral("statusBar"));
        LogicalExpressionClass->setStatusBar(statusBar);
        menuBar = new QMenuBar(LogicalExpressionClass);
        menuBar->setObjectName(QStringLiteral("menuBar"));
        menuBar->setGeometry(QRect(0, 0, 415, 26));
        menu = new QMenu(menuBar);
        menu->setObjectName(QStringLiteral("menu"));
        LogicalExpressionClass->setMenuBar(menuBar);

        menuBar->addAction(menu->menuAction());
        menu->addAction(actionintroduction);

        retranslateUi(LogicalExpressionClass);

        QMetaObject::connectSlotsByName(LogicalExpressionClass);
    } // setupUi

    void retranslateUi(QMainWindow *LogicalExpressionClass)
    {
        LogicalExpressionClass->setWindowTitle(QApplication::translate("LogicalExpressionClass", "\351\200\273\350\276\221\350\241\250\350\276\276\345\274\217\350\256\241\347\256\227\345\231\250", Q_NULLPTR));
        actionintroduction->setText(QApplication::translate("LogicalExpressionClass", "introduction", Q_NULLPTR));
        plainTextEdit->setPlainText(QString());
        pushButton_N->setText(QApplication::translate("LogicalExpressionClass", "\351\235\236", Q_NULLPTR));
        pushButton_left->setText(QApplication::translate("LogicalExpressionClass", "\345\267\246\346\213\254\345\217\267", Q_NULLPTR));
        pushButton_equ->setText(QApplication::translate("LogicalExpressionClass", "\347\255\211\344\273\267", Q_NULLPTR));
        pushButton_right->setText(QApplication::translate("LogicalExpressionClass", "\345\217\263\346\213\254\345\217\267", Q_NULLPTR));
        pushButton_imply->setText(QApplication::translate("LogicalExpressionClass", "\350\225\264\345\220\253", Q_NULLPTR));
        pushButton_input->setText(QApplication::translate("LogicalExpressionClass", "\350\276\223\345\205\245", Q_NULLPTR));
        pushButton_xor->setText(QApplication::translate("LogicalExpressionClass", "\345\274\202\346\210\226", Q_NULLPTR));
        pushButton_or->setText(QApplication::translate("LogicalExpressionClass", "\346\210\226", Q_NULLPTR));
        pushButton_profixTree->setText(QApplication::translate("LogicalExpressionClass", "\345\220\216\347\274\200\346\240\221", Q_NULLPTR));
        pushButton_profix->setText(QApplication::translate("LogicalExpressionClass", "\345\220\216\347\274\200\345\275\242\345\274\217", Q_NULLPTR));
        pushButton_table->setText(QApplication::translate("LogicalExpressionClass", "\347\234\237\345\200\274\350\241\250", Q_NULLPTR));
        pushButton_cal->setText(QApplication::translate("LogicalExpressionClass", "\350\256\241\347\256\227", Q_NULLPTR));
        pushButton_and->setText(QApplication::translate("LogicalExpressionClass", "\344\270\216", Q_NULLPTR));
        menu->setTitle(QApplication::translate("LogicalExpressionClass", "\345\205\263\344\272\216", Q_NULLPTR));
    } // retranslateUi

};

namespace Ui {
    class LogicalExpressionClass: public Ui_LogicalExpressionClass {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_LOGICALEXPRESSION_H

主类

直接将两个处理逻辑表达式的类当成了主类成员。
.h

#pragma once
#include <QtWidgets/QMainWindow>
#include<qsignalmapper.h>
#include<qtextdocument.h>
#include<qtimezone.h>
#include "ui_LogicalExpression.h"
#include"CheckAndTransfer.h"
#include"SolveLogExpression.h"
class LogicalExpression : public QMainWindow
{
	Q_OBJECT

public:
	LogicalExpression(QWidget *parent = Q_NULLPTR);
private slots:
	void clickString(QString);
	void clickInput();
	bool textchange();
	void PrintProfix();
	void PrintTree();
	void PrintTable();
	void PrintValue();
private:
	void setfirstLine(bool);
	void setOperatebutton(bool);
	bool resert;
	CheckAndTransfer checker;
	SolveLogExprssion solver;

	QSignalMapper* buttonMap;
	QString text;
	Ui::LogicalExpressionClass ui;
};

.cpp

#include "LogicalExpression.h"

LogicalExpression::LogicalExpression(QWidget *parent)
	: QMainWindow(parent)
{
	resert = 0;
	ui.setupUi(this);
	//运用QsinalMapper来避免类似的button每个都写一个函数
	//Qt特性connect,连接信号+动作
	buttonMap = new QSignalMapper(this);
	buttonMap->setMapping(ui.pushButton_and, (QString)"&");
	buttonMap->setMapping(ui.pushButton_or, (QString)"|");
	buttonMap->setMapping(ui.pushButton_xor, (QString)"^");
	buttonMap->setMapping(ui.pushButton_N, (QString)"~");
	buttonMap->setMapping(ui.pushButton_imply, (QString)"->");
	buttonMap->setMapping(ui.pushButton_equ, (QString)"=");
	buttonMap->setMapping(ui.pushButton_left, (QString)"(");
	buttonMap->setMapping(ui.pushButton_right, (QString)")");

	connect(ui.pushButton_and, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(ui.pushButton_or, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(ui.pushButton_xor, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(ui.pushButton_N, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(ui.pushButton_imply, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(ui.pushButton_equ, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(ui.pushButton_left, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(ui.pushButton_right, SIGNAL(clicked(bool)), buttonMap, SLOT(map()));
	connect(buttonMap, SIGNAL(mapped(QString)), this, SLOT(clickString(QString)));
	
	connect(ui.plainTextEdit, SIGNAL(textChanged()),this,SLOT(textchange()));
	connect(ui.pushButton_input, SIGNAL(clicked(bool)), this, SLOT(clickInput()));

	connect(ui.pushButton_profix, SIGNAL(clicked(bool)), this, SLOT(PrintProfix()));
	connect(ui.pushButton_profixTree, SIGNAL(clicked(bool)), this, SLOT(PrintTree()));
	connect(ui.pushButton_table, SIGNAL(clicked(bool)), this, SLOT(PrintTable()));
	connect(ui.pushButton_cal, SIGNAL(clicked(bool)), this, SLOT(PrintValue()));

	//一开始上排按钮不可用
	setfirstLine(false);
}
//实现点击添加逻辑运算符
void LogicalExpression::clickString(QString s)
{
	
	text = ui.plainTextEdit->toPlainText();
	text += s;
	ui.plainTextEdit->setPlainText(text);
}

void LogicalExpression::clickInput()
{
	//重新输入功能 
	if (resert)
	{
		setfirstLine(false);
		setOperatebutton(true);

		ui.plainTextEdit->clear();
		ui.plainTextEdit->setReadOnly(false);
		resert=0;
	}
	//输入当前表达式并检验是否合法
	else
	{
		checker.Init(text.toStdString());
		if (checker.Isvalid())
		{
			text += "\nValid Expression!\n";
			ui.plainTextEdit->setPlainText(text);
			
			//合法就只能操作上排功能按钮
			setOperatebutton(false);
			setfirstLine(true);
			
			
			solver.Init(checker.GetPostfix_Expression());
		}
		else {
			text += "\nInvalid Expression! Please input again\n";
			ui.plainTextEdit->setPlainText(text);
			//非法只能操作输入按钮重输
			setOperatebutton(false);
		}
	
		ui.plainTextEdit->setReadOnly(true);
		
		resert = 1;
	}
}
//保证text与Qplaintext内容一致
bool LogicalExpression::textchange()
{

	if (text == ui.plainTextEdit->toPlainText())
		return false;

	text = ui.plainTextEdit->toPlainText();
	return true;
}
//每个功能完成后都设置只读,以免修改
void LogicalExpression::PrintProfix()
{
	ui.plainTextEdit->setReadOnly(false);
	
	text.clear();
	//deque<string> 转化 string 转化 QString
	ElementSet profixSet = checker.GetPostfix_Expression();
	std::string profix="";
	for (auto i : profixSet)
		profix += i;
	
	text=QString::fromStdString(profix);
	ui.plainTextEdit->setPlainText(text);

	ui.plainTextEdit->setReadOnly(true);
}

void LogicalExpression::PrintTree()
{
	ui.plainTextEdit->setReadOnly(false);
	
	//ostringstream 转化 string 转化 QString
	std::ostringstream ostr;
	solver.PrintTree(ostr);
	std::string tree;
	tree = ostr.str();
	text=QString::fromStdString(tree);
	ui.plainTextEdit->setPlainText(text);

	ui.plainTextEdit->setReadOnly(true);

}

void LogicalExpression::PrintTable()
{
	ui.plainTextEdit->setReadOnly(false);


	std::ostringstream ostr;
	solver.PrintTable(ostr);
	std::string table = ostr.str();
	text = QString::fromStdString(table);
	ui.plainTextEdit->setPlainText(text);

	ui.plainTextEdit->setReadOnly(true);
}

void LogicalExpression::PrintValue()
{
	ui.plainTextEdit->setReadOnly(false);
	ui.plainTextEdit->clear();
	QString one = "1", zero = "0";
	for (auto& i : solver.VariableSet)
	{QString beforInput, value;
		do
		{
			beforInput = text;
			beforInput += QString::fromStdString(std::string("please input the logical value of " + i.first + ":\n"));
			
			text= beforInput;
			ui.plainTextEdit->setPlainText(text);

			ui.plainTextEdit->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
			//cursor.movePosition(QTextCursor::End);
			//ui.plainTextEdit->setTextCursor(cursor);
			QTime t;
			t.start();
			while(t.elapsed()<2000)
			{
				QCoreApplication::processEvents();
			}

			value = text[text.size() - 1];
		} while (text.size()-beforInput.size()!=1||( value != one && value != zero));

		i.second = value.toInt();
	}
	std::ostringstream ostr;
	solver.GetValue(ostr);
	text += QString::fromStdString(ostr.str());
	ui.plainTextEdit->setPlainText(text);
	ui.plainTextEdit->setReadOnly(true);
}
//设置第一排功能按钮
void LogicalExpression::setfirstLine(bool flag)
{
	ui.pushButton_profix->setEnabled(flag);
	ui.pushButton_profixTree->setEnabled(flag);
	ui.pushButton_cal->setEnabled(flag);
	ui.pushButton_table->setEnabled(flag);
}
//设置逻辑运算符按钮
void LogicalExpression::setOperatebutton(bool flag)
{
	ui.pushButton_and->setEnabled(flag);
	ui.pushButton_or->setEnabled(flag);
	ui.pushButton_xor->setEnabled(flag);
	ui.pushButton_N->setEnabled(flag);
	ui.pushButton_imply->setEnabled(flag);
	ui.pushButton_equ->setEnabled(flag);
	ui.pushButton_left->setEnabled(flag);
	ui.pushButton_right->setEnabled(flag);
}

基础类

用unorderedmap来储存运算符和优先级,对构成后缀有帮助。
用枚举类表示运算符,并使用不同的数字,使得可以运算符string可以使用switch(使用hash也可以达到一样的效果,不过这样更方便?)
用lambda表达式来定义函数,解决了重定义问题(原理是什么?求大佬指教)

#pragma once
#include<unordered_map>
#include<string>
namespace LogEpr {
	const std::string LEFT = "(", RIGHT = ")", NON = "~", AND = "&", OR = "|", XOR = "^", IMPLY = "->", EQUIVALENT = "=";
	enum class Logical_Priority :int
	{
		LEFT = -9,
		RIGHT = -10,
		NON = 0,
		AND = -1,
		OR = -2,
		XOR = -3,
		IMPLY = -4,
		EQUIVALENT = -5,
	};
	const std::unordered_map<std::string, int>Logical_Operators = { {LEFT,(int)Logical_Priority::LEFT},{RIGHT,(int)Logical_Priority::RIGHT},{NON,(int)Logical_Priority::NON},{AND,(int)Logical_Priority::AND},{OR,(int)Logical_Priority::OR},{XOR,(int)Logical_Priority::XOR},{IMPLY,(int)Logical_Priority::IMPLY},{EQUIVALENT,(int)Logical_Priority::EQUIVALENT} };
	auto Is_Logical_Operators = [&](std::string checkStr)->const bool {
		return Logical_Operators.find(checkStr) != Logical_Operators.end();
	};
}

树的节点类

#pragma once
#include<iostream>
#include<deque>
class node;
typedef std::shared_ptr<node> treeNode;
typedef std::string ElementType;
typedef std::deque<std::string> ElementSet;
class node {
public:
	static const int LEFT = 0;
	static const int RIGHT = 1;
	node() = default;
	node(ElementType e, treeNode l, treeNode r) {
		element = e;
		left = l;
		right = r;
	}
	node(ElementType e) :node(e, nullptr, nullptr) {}
	void giveChild(treeNode child, bool LeftOrRight) {
		if (LeftOrRight == 0)
			left = child;
		else right = child;
	}
	treeNode getChild(bool LeftOrRight)
	{
		if (LeftOrRight == 0)
			return left;
		else return right;
	}
	ElementType getElement()
	{
		return element;
	}
	~node() = default;
private:
	ElementType element;
	treeNode left, right;
};

前期检查类

实际上可以不把std::stack<std::string> Transfer当作类成员。
Init_Infix_Expression()能否可以用split函数来实现?
bool CheckAndTransfer::JudgeValid()的实现很巧妙来源,我做了一些修改,防止一个变量abc在变量a修改的时候被修改(实际上就是while改成if)。

#pragma once
#include"Logical_Expression.h"
#include<deque>
#include<stack>
#include <algorithm>
using namespace LogEpr;
class CheckAndTransfer
{
public:
	CheckAndTransfer() :IsValid(false) {  };

	void Init(std::string);
	bool Isvalid()
	{
		return IsValid;
	}
	std::deque<std::string> GetInfix_Expression()
	{
		return Infix_Expression;
	}
	std::deque<std::string> GetPostfix_Expression()
	{
		return Postfix_Expression;
	}
private:
	bool JudgeValid();
	void Init_Postfix_Expression();
	void Init_Infix_Expression();
	bool ChangeFragment(std::string& Modifiedstr, std::string Fragment, std::string modify);
	void push_pop();
	void Pop_Until_Less(std::string pushStr);

	bool IsValid;
	std::string Expression;
	std::stack<std::string> Transfer;
	std::deque<std::string>Infix_Expression, Postfix_Expression;
};
#include "CheckAndTransfer.h"
void CheckAndTransfer::Init(std::string expression) {
	Expression = expression;
	Init_Infix_Expression();
	IsValid = 0;
	if (JudgeValid())
	{
		IsValid = 1;
		//合法才能构建后缀表达式
		Init_Postfix_Expression();
	}
}

void CheckAndTransfer::Init_Infix_Expression()
{
	if (!Infix_Expression.empty())
		Infix_Expression.clear();

	for (auto i = Expression.cbegin(); i != Expression.cend(); ++i)
	{
		auto j = i;
		//每个逻辑运算符作为一个中断
		while ((j + 1) != Expression.end()
			&& !Is_Logical_Operators({ *j }))
			++j;
		if (i == j)
		{
			Infix_Expression.push_back({ *j });
		}
		else
		{
			auto k = i;
			//判断中间有没有->
			for (auto l = i; l + 1 <= j; ++l)
			{
				if (Is_Logical_Operators({ l,l + 1 }))
					k = l + 1;
			}
			if (k == i)
			{
				Infix_Expression.push_back({ i,j });
				Infix_Expression.push_back({ *j });
			}
			else {
				Infix_Expression.push_back({ i,k-1 });
				Infix_Expression.push_back({ k-1,k });
				Infix_Expression.push_back({ k,j });
			}
		}
		i = j;
	}

}

bool CheckAndTransfer::JudgeValid()
{
	const std::string ValidResult = "1";
	const std::vector<std::string> Valid_Equation = { "~1","(1)","1&1","1|1","1^1","1->1","1=1" };

	for (auto i : Infix_Expression)
	{
		if (!Is_Logical_Operators(i))
			ChangeFragment(Expression, i, ValidResult);
	}
	std::for_each(Infix_Expression.begin(), Infix_Expression.end(),
		[this, ValidResult](const std::string s)->void
		{
			if (!Is_Logical_Operators(s))
				ChangeFragment(Expression, s, ValidResult);
		}
	);

	//若没有修改则循环停止
	int flag = 1;
	while (flag)
	{
		flag = 0;
		for (auto i : Valid_Equation)
			flag += ChangeFragment(Expression, i, ValidResult);
	}

	if (Expression == ValidResult)
		return true;
	else return false;

}
bool CheckAndTransfer::ChangeFragment(std::string& Modifiedstr, std::string Fragment, std::string modify)
{
	bool IsModified = 0;
	auto p = Modifiedstr.find(Fragment);
	if (p != Modifiedstr.npos)
	{
		IsModified = 1;
		Modifiedstr.replace(p, Fragment.length(), modify);
	}
	return IsModified;
}

void CheckAndTransfer::Init_Postfix_Expression()
{
	if (!Postfix_Expression.empty())
		Postfix_Expression.clear();

	for (auto i : Infix_Expression)
	{
		if (Is_Logical_Operators(i))
		{
			switch ((Logical_Priority)Logical_Operators.at(i))
			{
			//左右括号特殊处理
			case Logical_Priority::LEFT:
				Transfer.push(i);
				break;
			case Logical_Priority::RIGHT:
				while (Transfer.top() != "(")
					push_pop();

				Transfer.pop();
				break;
			default: Pop_Until_Less(i);
				break;
			}
		}
		//元素直接加入
		else Postfix_Expression.push_back(i);
	}
	while (!Transfer.empty())
		push_pop();

}
inline void CheckAndTransfer::push_pop()
{
	Postfix_Expression.push_back(Transfer.top());
	Transfer.pop();
}
//将优先级更高的出栈
void CheckAndTransfer::Pop_Until_Less(std::string pushStr)
{
	while (!Transfer.empty()
		&& Logical_Operators.at(Transfer.top()) > Logical_Operators.at(pushStr))
		push_pop();
	Transfer.push(pushStr);
}

后缀二叉树类

简单使用了一下智能指针
横向打印二叉树借鉴代码,修改添加了一个stringstream参数来保存打印结果。

#pragma once
#include"Node.h"
#include"Logical_Expression.h"
#include<stack>
#include<sstream>
using namespace LogEpr;
class LogPostBiTree {
public:
	LogPostBiTree(ElementSet data) { Init(data); };
	void Init(ElementSet);
	LogPostBiTree() {};
	void PrintTree(std::ostringstream&os);
private:
	void print_tree(treeNode, treeNode, std::string&, std::ostringstream&);
protected:
	treeNode Tree;
};
#include "LogPostBiTree.h"
void LogPostBiTree::Init(ElementSet data)
{
	//使用栈来初始化
	std::stack<treeNode> NodeStack;
	for (auto i : data)
	{
		treeNode temp = std::make_shared<node>(i);
		if (!Is_Logical_Operators(i))
			NodeStack.push(temp);
		else if (i != NON)
		{
			temp->giveChild(NodeStack.top(), temp->LEFT);
			NodeStack.pop();
			temp->giveChild(NodeStack.top(), temp->RIGHT);
			NodeStack.pop();
			NodeStack.push(temp);
		}
		else {
			//非只添加到左边
			temp->giveChild(NodeStack.top(), temp->LEFT);
			NodeStack.pop();
			NodeStack.push(temp);
		}

		//将树根定位到最后一个元素指针上
		if (i == data.back())
			Tree = temp;
	}
}
void LogPostBiTree::PrintTree(std::ostringstream& os)
{
	std::string start = "";
	print_tree(Tree, Tree, start, os);
}
void LogPostBiTree::print_tree(treeNode parent, treeNode root, std::string& prefix,std::ostringstream& os) {
	prefix += "+";
	if (root) {
		os << prefix << "##" << root->getElement() << "\n";
		if (root == parent || root == parent->getChild(parent->RIGHT)) {
			prefix.pop_back(); prefix += " ";
		}
		std::string pre = prefix + "  ";
		print_tree(root, root->getChild(root->LEFT), pre,os);
		print_tree(root, root->getChild(parent->RIGHT), pre,os);
	}
	else {
		if (parent->getChild(root->LEFT) || parent->getChild(parent->RIGHT)) 
			//有一个孩子节点不空就打印,以区分左右孩子
			os << prefix << "##" << "{}" << "\n";
	}

}

求值类

在解决打印真值表的时候,一开始的想法是递归,后来发现元素的真值数列是二进制,且对应的十进制的值为从0到\(2^n-1\),因此采用bitset类很容易的解决了。

#pragma once
#include"LogPostBiTree.h"
#include<bitset>
class SolveLogExprssion :public LogPostBiTree {
public:
	static const int maxValueNumber = 100;
	SolveLogExprssion(ElementSet set) :LogPostBiTree(set) {
		Init(set);
	}
	void Init(ElementSet set) {
		LogPostBiTree::Init(set);
		for (auto i : set)
			if (!Is_Logical_Operators(i))
				VariableSet[i] = 0;
	}
	SolveLogExprssion() {};
	void GetValue(std::ostringstream& );
	void PrintTable(std::ostringstream&);
	std::unordered_map<std::string, bool> VariableSet;
private:
	int Calculate(treeNode);
	
};
#include "SolveLogExpression.h"
void SolveLogExprssion::GetValue(std::ostringstream& os)
{
	/*for (auto i : VariableSet)
		std::cout << i.second << std::endl;*/
	os << "The value of expression is:" << Calculate(Tree) << "\n";
}
void SolveLogExprssion::PrintTable(std::ostringstream& os)
{
	//biset储存各元素值
	std::bitset<maxValueNumber> bs;
	
	//表头
	for (auto i : VariableSet)
		os << i.first << " ";
	os << "Value\n";

	int Amount = VariableSet.size();
	int p = 0;
	//各元素真值变化并输出结果
	for (int i = 0; i < (1 << Amount); ++i)
	{
		p = 0;
		for (auto j = VariableSet.begin(); j != VariableSet.end(); ++j, ++p)
		{
			os << bs[p] << " ";
			j->second = bs[p];
		}
		os << Calculate(Tree) << "\n";
		bs = i;
	}
}
int SolveLogExprssion::Calculate(treeNode sum)
{
	//如果非空
	if (sum)
	{
		//如果是元素就直接返还元素值
		std::string element = sum->getElement();
		if (!Is_Logical_Operators(element))
			return VariableSet[element];
		else {
			//逻辑运算符则进行运算
			int left = Calculate(sum->getChild(sum->LEFT));
			int right = Calculate(sum->getChild(sum->RIGHT));
			switch ((Logical_Priority)Logical_Operators.at(element))
			{
			case Logical_Priority::NON:
				return ~left;
			case Logical_Priority::AND:
				return left & right;
			case Logical_Priority::OR:
				return left | right;
			case Logical_Priority::XOR:
				return left ^ right;
			case Logical_Priority::IMPLY:
				return (~left) | right;
			case Logical_Priority::EQUIVALENT:
				return left == right;
			default:
				return -1;
				break;
			}
		}
	}
	else return -1;
}

main类

#include "LogicalExpression.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	LogicalExpression w;
	

	w.show();

	return a.exec();
}

初版

没有实现可视化功能,也没有菜单。但输入变量值并计算真值上没有问题。

#pragma warning(disable:4996)
#include <iostream>
#include"CheckAndTransfer.h"
#include"SolveLogExpression.h"

int main()
{
	CheckAndTransfer checker;
	std::string expression;
	while (true)
	{
		std::cout << "输入表达式:" << std::endl;
		std::getline(std::cin, expression);
		checker.Init(expression);
		/*
		for (auto i : check.GetInfix_Expression())
			std::cout << i << " ";
		*/
		
		if (!checker.Isvalid())
			std::cout << "Invalid expression!";
		else break;
	}
	/*
	for (auto i : check.GetPostfix_Expression())
	std::cout << i << " "<<std::endl;
	*/
	
	SolveLogExprssion solver(checker.GetPostfix_Expression());
	
	solver.PrintTree();
	solver.GetValue(std::cin);
	solver.PrintTable();

	return 0;
}
#pragma once
#include<unordered_map>
#include<string>
namespace LogEpr {
	const std::string LEFT = "(", RIGHT = ")", NON = "~", AND = "&", OR = "|", XOR = "^", IMPLY = "->", EQUIVALENT = "=";
	enum class Logical_Priority :int
	{
		LEFT = -9,
		RIGHT = -10,
		NON = 0,
		AND = -1,
		OR = -2,
		XOR = -3,
		IMPLY = -4,
		EQUIVALENT = -5,
	};
	const std::unordered_map<std::string, int>Logical_Operators = { {LEFT,(int)Logical_Priority::LEFT},{RIGHT,(int)Logical_Priority::RIGHT},{NON,(int)Logical_Priority::NON},{AND,(int)Logical_Priority::AND},{OR,(int)Logical_Priority::OR},{XOR,(int)Logical_Priority::XOR},{IMPLY,(int)Logical_Priority::IMPLY},{EQUIVALENT,(int)Logical_Priority::EQUIVALENT} };
	auto Is_Logical_Operators = [&](std::string checkStr)->const bool {
		return Logical_Operators.find(checkStr) != Logical_Operators.end();
	};
}
#pragma once
#include"Logical_Expression.h"
#include<deque>
#include<stack>
#include <algorithm>
using namespace LogEpr;
class CheckAndTransfer
{
public:
	CheckAndTransfer() :IsValid(false) {  };

	void Init(std::string);
	bool Isvalid()
	{
		return IsValid;
	}
	std::deque<std::string> GetInfix_Expression()
	{
		return Infix_Expression;
	}
	std::deque<std::string> GetPostfix_Expression()
	{
		return Postfix_Expression;
	}
private:
	bool JudgeValid();
	void Init_Postfix_Expression();
	void Init_Infix_Expression();
	bool ChangeFragment(std::string& Modifiedstr, std::string Fragment, std::string modify);
	void push_pop();
	void Pop_Until_Less(std::string pushStr);

	bool IsValid;
	std::string Expression;
	std::stack<std::string> Transfer;
	std::deque<std::string>Infix_Expression, Postfix_Expression;
};
#include "CheckAndTransfer.h"
void CheckAndTransfer::Init(std::string expression) {
	Expression = expression;
	Init_Infix_Expression();
	IsValid = 0;
	if (JudgeValid())
	{
		IsValid = 1;
		Init_Postfix_Expression();
	}
}
bool CheckAndTransfer::ChangeFragment(std::string& Modifiedstr, std::string Fragment, std::string modify)
{
	bool IsModified = 0;
	auto p = Modifiedstr.find(Fragment);
	if (p != Modifiedstr.npos)
	{
		IsModified = 1;
		Modifiedstr.replace(p, Fragment.length(), modify);
	}
	return IsModified;
}
inline void CheckAndTransfer::push_pop()
{
	Postfix_Expression.push_back(Transfer.top());
	Transfer.pop();
}
void CheckAndTransfer::Pop_Until_Less(std::string pushStr)
{
	while (!Transfer.empty()
		&& Logical_Operators.at(Transfer.top()) > Logical_Operators.at(pushStr))
		push_pop();
	Transfer.push(pushStr);
}
void CheckAndTransfer::Init_Infix_Expression()
{
	if (!Infix_Expression.empty())
		Infix_Expression.clear();
	for (auto i = Expression.cbegin(); i != Expression.cend(); ++i)
	{
		auto j = i;
		while ((j + 1) != Expression.end()
			&& !Is_Logical_Operators({ *j }))
			++j;
		
		auto k = i;
		for (auto l = i; l + 1 <= j; ++l)
		{
			if (Is_Logical_Operators({ l,l + 1 }))
				k = l + 1;
		}
		if (k == i)
		{
			Infix_Expression.push_back({ i,j });
			Infix_Expression.push_back({ *j });
		}
		else {
			Infix_Expression.push_back({ i,k - 1 });
			Infix_Expression.push_back({ k - 1,k });
			Infix_Expression.push_back({ k,j });
		}
		i = j;
	}

}
bool CheckAndTransfer::JudgeValid()
{
	const std::string ValidResult = "1";
	const std::vector<std::string> Valid_Equation = { "~1","(1)","1&1","1|1","1^1","1->1","1=1" };

	for (auto i : Infix_Expression)
	{
		if (!Is_Logical_Operators(i))
			ChangeFragment(Expression, i, ValidResult);
	}
	std::for_each(Infix_Expression.begin(), Infix_Expression.end(),
		[this, ValidResult](const std::string s)->void
		{
			if (!Is_Logical_Operators(s))
				ChangeFragment(Expression, s, ValidResult);
		}
	);

	int flag = 1;
	while (flag)
	{
		flag = 0;
		for (auto i : Valid_Equation)
			flag += ChangeFragment(Expression, i, ValidResult);
	}

	if (Expression == ValidResult)
		return true;
	else return false;

}
void CheckAndTransfer::Init_Postfix_Expression()
{
	if (!Postfix_Expression.empty())
		Postfix_Expression.clear();
	for (auto i : Infix_Expression)
	{
		if (Is_Logical_Operators(i))
		{
			switch ((Logical_Priority)Logical_Operators.at(i))
			{
			case Logical_Priority::LEFT:
				Transfer.push(i);
				break;
			case Logical_Priority::RIGHT:
				while (Transfer.top() != "(")
					push_pop();

				Transfer.pop();
				break;
			default: Pop_Until_Less(i);
				break;
			}
		}
		else Postfix_Expression.push_back(i);
	}
	while (!Transfer.empty())
		push_pop();

}
#pragma once
#include<iostream>
#include<deque>
class node;
typedef std::shared_ptr<node> treeNode;
typedef std::string ElementType;
typedef std::deque<std::string> ElementSet;
class node {
public:
	static const int LEFT = 0;
	static const int RIGHT = 1;
	node() = default;
	node(ElementType e, treeNode l, treeNode r) {
		element = e;
		left = l;
		right = r;
	}
	node(ElementType e) :node(e, nullptr, nullptr) {}
	void giveChild(treeNode child, bool LeftOrRight) {
		if (LeftOrRight == 0)
			left = child;
		else right = child;
	}
	treeNode getChild(bool LeftOrRight)
	{
		if (LeftOrRight == 0)
			return left;
		else return right;
	}
	ElementType getElement()
	{
		return element;
	}
	~node() = default;
private:
	ElementType element;
	treeNode left, right;
};
#pragma once
#include"Node.h"
#include"Logical_Expression.h"
#include<stack>
using namespace LogEpr;
class LogPostBiTree {
public:
	LogPostBiTree(ElementSet data);
	void PrintTree();
private:
	void print_tree(treeNode, treeNode, std::string&);
protected:
	treeNode Tree;
};
#include "LogPostBiTree.h"
LogPostBiTree::LogPostBiTree(ElementSet data)
{
	std::stack<treeNode> NodeStack;
	for (auto i : data)
	{
		treeNode temp = std::make_shared<node>(i);
		if (!Is_Logical_Operators(i))
			NodeStack.push(temp);
		else if (i != NON)
		{
			temp->giveChild(NodeStack.top(), temp->LEFT);
			NodeStack.pop();
			temp->giveChild(NodeStack.top(), temp->RIGHT);
			NodeStack.pop();
			NodeStack.push(temp);
		}
		else {
			temp->giveChild(NodeStack.top(), temp->LEFT);
			NodeStack.pop();
			NodeStack.push(temp);
		}

		if (i == data.back())
			Tree = temp;
	}
}
void LogPostBiTree::PrintTree()
{
	std::string start = "";
	print_tree(Tree, Tree, start);
}
void LogPostBiTree::print_tree(treeNode parent, treeNode root, std::string& prefix) {
	prefix += "+";
	if (root) {
		std::cout << prefix << "##" << root->getElement() << std::endl;
		if (root == parent || root == parent->getChild(parent->RIGHT)) {
			prefix.pop_back(); prefix += " ";
		}
		std::string pre = prefix + "  ";
		print_tree(root, root->getChild(root->LEFT), pre);
		print_tree(root, root->getChild(parent->RIGHT), pre);
	}
	else {
		if (parent->getChild(root->LEFT) || parent->getChild(parent->RIGHT)) //有一个孩子节点不空就打印,以区分左右孩子
			std::cout << prefix << "##" << "{}" << std::endl;
	}

}
#pragma once
#include"LogPostBiTree.h"
#include<bitset>
class SolveLogExprssion :public LogPostBiTree {
public:
	SolveLogExprssion(ElementSet set) :LogPostBiTree(set) {
		for (auto i : set)
			if (!Is_Logical_Operators(i))
				VariableSet[i] = 1;
	}
	void GetValue(std::istream& is);
	void PrintTable();
private:
	int Calculate(treeNode);
	std::unordered_map<std::string, bool> VariableSet;
};
#include "SolveLogExpression.h"
void SolveLogExprssion::GetValue(std::istream& is = std::cin)
{
	std::string valueLine;
	for (auto& i : VariableSet)
	{
		do
		{
			std::cout << "请输入" << i.first << "的逻辑值:" << std::endl;
			std::getline(is, valueLine);
		} while (valueLine.size() > 1 && valueLine[0] != \'1\' && valueLine[0] != \'0\');
		
		i.second = std::stoi(valueLine);
		std::cout << i.first << "的逻辑值为:" << i.second << std::endl;
	}
	for (auto i : VariableSet)
		std::cout << i.second << std::endl;
	std::cout << "表达式的真值为:" << Calculate(Tree) << std::endl;
}
void SolveLogExprssion::PrintTable()
{
	std::bitset<100> bs;
	for (auto i : VariableSet)
		std::cout << i.first << " ";
	std::cout << "真值" << std::endl;
	int Amount = VariableSet.size();
	int p = 0;
	for (int i = 0; i < (1 << Amount); ++i)
	{
		p = 0;
		for (auto j = VariableSet.begin(); j != VariableSet.end(); ++j, ++p)
		{
			std::cout << bs[p] << " ";
			j->second = bs[p];
		}
		std::cout << Calculate(Tree) << std::endl;
		bs = i;
	}
}
int SolveLogExprssion::Calculate(treeNode sum)
{
	if (sum)
	{
		std::string element = sum->getElement();
		if (!Is_Logical_Operators(element))
			return VariableSet[element];
		else {
			int left = Calculate(sum->getChild(sum->LEFT));
			int right = Calculate(sum->getChild(sum->RIGHT));
			switch ((Logical_Priority)Logical_Operators.at(element))
			{
			case Logical_Priority::NON:
				return ~left;
			case Logical_Priority::AND:
				return left & right;
			case Logical_Priority::OR:
				return left | right;
			case Logical_Priority::XOR:
				return left ^ right;
			case Logical_Priority::IMPLY:
				return (~left) | right;
			case Logical_Priority::EQUIVALENT:
				return left == right;
			default:
				return -1;
				break;
			}
		}
	}
	else return -1;
}

总结

每看一次代码就有改进代码的想法,可惜实力和时间有限。按照某帖子的评价,现在已经达到了C++第一阶段C with classes,将各种东西包装成class,对于第二阶段继承、虚函数、多继承、虚继承,仅仅是知道这些名词,至于在什么样的状况下使用,怎么使用,还没有头绪。在java中,了解到接口的思想,C++应该也可以使用。《C++ primer》还有一章模板,学会使用后,也能提高代码重用度。学校学习的过程中缺少很多工程的能力,平时应该注意一下,对于做事效率也有帮助。在写代码的过程中,对很多已知的知识有了新的理解,实践,有挑战性的创造,果然是掌握技术的最好方法,以后也要主动找项目练手。虽然有很多方向,但可惜短时间内不会再主动用C++11了,以后写的C++代码可能仅仅是C+STL。

分类:

技术点:

相关文章:

  • 2021-12-23
  • 2021-12-06
  • 2021-10-14
  • 2022-02-18
  • 2022-12-23
  • 2022-01-30
  • 2022-01-14
猜你喜欢
  • 2021-12-06
  • 2021-12-19
  • 2021-12-23
  • 2021-12-02
  • 2022-12-23
  • 2021-06-10
相关资源
相似解决方案