访问者模式-二叉树数据结构-求出四则混合运算表达式的值
kagula
2019-3-24
最后更新日期: 2020-6-29
更新次数: 1
正文
演示如何使用访问者模式划分功能块.
main.cpp
演示如何调用
#include <cstdlib>
#include <string>
#include <iostream>
#include "Node.h"
#include "Builder.h"
#include "ExpBuilder.h"
#include "ExpParser.h"
#include "StackBasedCalVisitor.h"
using namespace std;
int main(int argc, char** argv) {
ExpBuilder builder;
ExpParser parser;
parser.setBuilder(&builder);
string input="(((30+500)*70)-(60/50))";//输入表达式
parser.parse(input);
Node* root = builder.getExpression();
cout<<input<<endl;
root->print();
StackBasedCalVisitor sbsv;
root->Accept(&sbsv);
cout << endl << "StackBasedSumVisitor Result: " << sbsv.getResult() << endl;
assert(37098.9 == sbsv.getResult());//验证运算结果
return 0;
}
UML类图
用Visio2019画了下结构图,感觉效果不太好, 最后改用Visual Paradigm。
下面是依赖文件
Builder.h
#ifndef BUILDER_H
#define BUILDER_H
#include "Node.h"
#include <string>
class Builder{
public:
virtual void addOperand(string operand)=0;
virtual void addLiteral(string literal)=0;
virtual void addLeftParenthesis()=0;
virtual void addRightParenthesis()=0;
virtual Node* getExpression()=0;
};
#endif /* BUILDER_H */
ExpBuilder.h
#ifndef EXPRESSIONBUILDER_H
#define EXPRESSIONBUILDER_H
#include <stack>
#include <iostream>
#include <assert.h>
#include "Node.h"
#include "Builder.h"
class ExpBuilder: public Builder{
protected:
stack<Node*> expStack;
public:
~ExpBuilder()
{
}
void addOperand(string operand) override{
expStack.push(new Node(operand));
};
void addLiteral(string literal) override {
Node* newLiteral = new Node(literal);
if (expStack.size() == 0)
{
expStack.push(newLiteral);
return;
}
else if (expStack.size() == 1)
{
expStack.push(newLiteral);
return;
}
else if (expStack.size() >= 2)
{
if (isON())
{
Node* root = expStack.top(); expStack.pop();
root->setLeftChild(newLiteral);
//because division's result depend the order, so right element must be in right child node.
Node* rightNode = expStack.top(); expStack.pop();
root->setRightChild(rightNode);
expStack.push(root);
return;
}
expStack.push(newLiteral);
}
};
void addLeftParenthesis() override {
if (expStack.size() == 0)
{
assert(NULL);
}
else if (expStack.size() == 1)
{
Node* rightParenthesis = expStack.top(); expStack.pop();
if (rightParenthesis->getValue() == ")")
return;
assert(NULL);
}
else if (expStack.size() >= 2)
{
Node* root = expStack.top(); expStack.pop();
Node* rightParenthesis = expStack.top(); expStack.pop();
if (rightParenthesis->getValue() == ")")
{
expStack.push(root);
}
else if (rightParenthesis->getValue() == "+" ||
rightParenthesis->getValue() == "-" ||
rightParenthesis->getValue() == "*" ||
rightParenthesis->getValue() == "/" )
{
Node* pOperator = rightParenthesis;
pOperator->setLeftChild(root);
Node* rightNumber = expStack.top(); expStack.pop();
pOperator->setRightChild(rightNumber);
Node* rightParenthesis = expStack.top(); expStack.pop();
if (rightParenthesis->getValue() != ")")
{
assert(NULL);
}
expStack.push(pOperator);
}
else
{
assert(NULL);
}
}
};
void addRightParenthesis() override {
expStack.push(new Node(")"));
};
Node* getExpression() override {
if (expStack.size() > 0)
{
Node* root = expStack.top();
expStack.pop();
return root;
}
return nullptr;
};
private:
//Operator, Number
bool isON()
{
Node* maybeO = expStack.top(); expStack.pop();
Node* maybeN = expStack.top(); expStack.push(maybeO);
bool isOperator = false;
if (maybeO->getValue() == "+" || maybeO->getValue() == "-" || maybeO->getValue() == "*" || maybeO->getValue() == "/")
{
isOperator = true;
}
bool isNumber = this->isNumber(maybeN->getValue());
if (isOperator && isNumber)
{
return true;
}
return false;
}
bool isNumber(std::string str)
{
for (int i = 0; i < str.size(); i++)
{
if ( (str[i] <= '9' && str[i] >= '0') || str[i] == '.')
continue;
return false;
}
return true;
}
};
#endif /* EXPBUILDER_H */
ExpParser.h
#ifndef PARSER_H
#define PARSER_H
#include <string>
#include <iostream>
#include "Builder.h"
#include <list>
enum class DATA_TYPE {
UNKNOWN, DIGITAL, OPERATOR, PARENTHESIS
};
class ExpParser {
protected:
Builder* m_expBuilder;
public:
void setBuilder(ExpBuilder* builder) {
m_expBuilder = builder;
}
void parse(string exp) {
//transform to element list
auto filter = [](ExpParser *pThis, string exp)->std::list<string>
{
list<string> listResult;
string strLiteral;
int i = exp.size() - 1;
while (i >= 0)
{
const char cTemp = exp[i];
if (pThis->getType(cTemp) == DATA_TYPE::PARENTHESIS || pThis->getType(cTemp) == DATA_TYPE::OPERATOR)
{
if (strLiteral.empty() == false)
{
listResult.push_back(strLiteral), strLiteral = "";
}
listResult.push_back(pThis->c2str(cTemp));
}
else if (pThis->getType(cTemp) == DATA_TYPE::DIGITAL)
{
strLiteral.insert(0, pThis->c2str(cTemp));
}
i--;
}//while
if (strLiteral.empty() == false)
{
listResult.push_back(strLiteral), strLiteral = "";
}
return listResult;
};
list<string> elements = filter(this, exp);
//store to binary tree
for (list<string>::iterator iter = elements.begin();
iter != elements.end(); iter++)
{
string element = *iter;
if (getType(element) == DATA_TYPE::PARENTHESIS)
{
if (element[0] == '(')
{
m_expBuilder->addLeftParenthesis();
}
else if (element[0] == ')')
{
m_expBuilder->addRightParenthesis();
}
}
else if (getType(element) == DATA_TYPE::OPERATOR)
{
m_expBuilder->addOperand(c2str(element[0]));
}
else if (getType(element) == DATA_TYPE::DIGITAL)
{
m_expBuilder->addLiteral(element);
}
}
}
private:
DATA_TYPE getType(const char c)
{
if ((c == '+') || (c == '-') || (c == '*') || (c == '/'))
{
return DATA_TYPE::OPERATOR;
}
if ((c == '(') || (c == ')'))
{
return DATA_TYPE::PARENTHESIS;
}
if (isdigit(c))
return DATA_TYPE::DIGITAL;
return DATA_TYPE::UNKNOWN;
}
DATA_TYPE getType(const string c)
{
if (c.size() == 0)
{
assert(NULL);
}
if (c.size()==1)
{
return getType(c[0]);
}
for (size_t i = 0; i < c.size(); i++)
{
if (getType(c[i]) != DATA_TYPE::DIGITAL)
return DATA_TYPE::UNKNOWN;
}
return DATA_TYPE::DIGITAL;
}
string c2str(const char c)
{
string strT;
strT.push_back(c);
return strT;
}
};
#endif /* EXPPARSER_H */
Node.h
#ifndef NODE_H
#define NODE_H
#include <string>
#include <iostream>
#include "Visitor.h"
using namespace std;
class Node{
protected:
string m_value;
Node* m_leftChild;
Node* m_rightChild;
public:
Node(string value):m_value(value), m_leftChild(NULL), m_rightChild(NULL){}
virtual ~Node(){
if(!m_leftChild) delete m_leftChild;
if(!m_rightChild) delete m_rightChild;
}
string getValue(){
return m_value;
}
void setLeftChild(Node* left){
m_leftChild = left;
}
void setRightChild(Node* right){
m_rightChild = right;
}
Node* getLeftChild() const {
return m_leftChild;
}
Node* getRightChild() const {
return m_rightChild;
}
void print(){
if(m_leftChild){
cout<<"(";
m_leftChild->print();
}
cout<<m_value;
if(m_rightChild){
m_rightChild->print();
cout<<")";
}
}
void Accept(Visitor* visitor){
visitor->getNum(this);
}
};
#endif /* NODE_H */
Visitor.h
#ifndef VISITOR_H
#define VISITOR_H
class Node;
class Visitor{
public:
Visitor() {}
virtual ~Visitor() {}
double getResult() {
return result;
}
virtual void getNum(Node *ptr) = 0;
protected:
double result = 0;
};
#endif /* VISITOR_H */
StackBasedCalVisirot.h
#ifndef STACKBASEDCALVISITOR_H
#define STACKBASEDCALVISITOR_H
#include "Node.h"
#include "Visitor.h"
#include <stack>
class StackBasedCalVisitor:public Visitor{
public:
StackBasedCalVisitor() {};
virtual ~StackBasedCalVisitor() {};
void getNum(Node *ptr) override{
result = calculate(ptr);
}
double calculate(Node *pNode)
{
if (pNode->getValue()=="+")
{
return calculate(pNode->getLeftChild()) + calculate(pNode->getRightChild());
}
else if (pNode->getValue() == "-")
{
return calculate(pNode->getLeftChild()) - calculate(pNode->getRightChild());
}
else if (pNode->getValue() == "*")
{
return calculate(pNode->getLeftChild()) * calculate(pNode->getRightChild());
}
else if (pNode->getValue() == "/")
{
return calculate(pNode->getLeftChild()) / calculate(pNode->getRightChild());
}
double dVal = std::stod(pNode->getValue());
if (pNode->getLeftChild() != nullptr)
{
assert(NULL);
}
if (pNode->getRightChild() != nullptr)
{
assert(NULL);
}
return dVal;
}
};
#endif /* STACKBASEDCALVISITOR_H */
类似的需求,比如说实现这个算法,更适合采用工作流的方法来进行设计, 现在这种样子, 代码看上去反而不太容易理解。我觉的面向对象设计只适合偏向业务流需求的解。