1.让表达式计算器支持变量赋值
-
eg:a=5,解析成一个表达式树应该如下,其中a变量的值应该从Storage类中去获取
AssignNode节点的Calc方法,比如:x=5,=号节点的值,其实等于x节点,也等于5节点,首先取出右节点的值,然后调用左节点的Assign方法
-
类图如下:
新增变量节点VariableNode,VariableNode继承至Node,在Node中新增Assign方法,用以变量赋值,IsValue判断变量是左值,其他节点都不是左值;
新增赋值运算节点AssignNode,它是二元运算节点,其左节点是变量,右节点是数字节点,所以继承至二元运算节点BinaryNode;
-
startUML,只画新增的
P46\Node.h
#ifndef _NODE_H
#define _NODE_H
#include <vector>
#include <cassert>
class Storage;
//(1)【采用】禁止对象拷贝的eg演示
//Noncopyable不能构造对象,因为构造对象没意义,仅仅用来继承
class Noncopyable
{
protected:
Noncopyable() {};
~Noncopyable() {};
private:
Noncopyable(const Noncopyable&);
const Noncopyable& operator=(const Noncopyable&);
};
//用private继承的原因是:并没有继承Noncopyable类的接口,即:这不是接口继承,而是实现继承
//实现继承:仅仅利用基类的内部函数,仅仅能在派生类的内部使用,并不能成为派生类额接口
//Node变成了对象语义,因为:要构造Node,要先构造Noncopyable,而Noncopyable既不能拷贝构造,也不能赋值了
class Node : private Noncopyable
{
public:
//每个节点都有一个计算的纯虚函数
//类Node用于多态,派生类都要实现Calc
//Calc声明为const的,因为Calc不会改变类的成员
virtual double Calc() const = 0;
//类Node是多态类,析构函数也要声明为虚析构函数,否则基类指针指向派生类对象,
//通过基类指针释放对象的时候,是不会调用派生类的析构函数
//默认节点都不是左值,只有变量节点是左值
virtual bool IsLvalue() const
{
return false;
}
//只有变量节点有赋值的方法,其他节点都没有
virtual void Assign(double)
{
assert(!"Assign called incorrectlly");//“XX”字符串是真的,取一个!为假的,默认情况下断言为假的
}
virtual ~Node() {};
};
//NumerNode要实现这个纯虚函数Calc,为具体类;若没实现,还是抽象类
class NumberNode : public Node
{
public:
NumberNode(double number) : number_(number) {}
double Calc() const;
private:
const double number_;//加const的原因:因为number_初始化后就不会改变
};
//BinaryNode节点有2个子节点
//BinaryNode类没有实现Calc方法,BinaryNode类仍然是抽象类,只有它的派生类,加、减、乘、除节点才知道该如何计算
class BinaryNode : public Node
{
public:
BinaryNode(Node* left, Node* right)
: left_(left), right_(right) {}
~BinaryNode();//记得要释放left_和right_节点
protected:
Node* const left_;//const的作用:指针不能改变(即指针不能指向其它的节点),而不是指针所指向的内容不能改变
Node* const right_;
};
//与BinaryNode相比,它只有1个孩子
//UnaryNod也是抽象类,因为它没有实现Calc方法
class UnaryNode : public Node
{
public:
UnaryNode(Node* child)
: child_(child) {}
~UnaryNode();
protected:
Node* child_;
}
// //加法运算节点AddNode
// class AddNode : public BinaryNode
// {
// public:
// //构造函数初始化,要调用基类部分的构造函数
// AddNode(Node* left, Node* right)
// : BinaryNode(left, right) {}
// //要实现Calc方法,AddNode类是具体类
// double Calc() const;
// };
// class SubNode : public BinaryNode
// {
// public:
// SubNode(Node* left, Node* right)
// : BinaryNode(left, right) {}
// double Calc() const;
// };
// class MultiplyNode : public BinaryNode
// {
// public:
// MultiplyNode(Node* left, Node* right)
// : BinaryNode(left, right) {}
// double Calc() const;
// };
// class DivideNode : public BinaryNode
// {
// public:
// DivideNode(Node* left, Node* right)
// : BinaryNode(left, right) {}
// double Calc() const;
// };
class UminusNode : public UnaryNode
{
public:
UminusNode(Node* child)
: UnaryNode(child) {}
double Calc() const;
};
//MultiplyNode没有实现Calc方法,还是一个抽象类
class MultipleNode : public Node
{
public:
MultipleNode(Node*)
{
//添加第一个节点。第一个节点总是正的,比如-7-5+1,该-7就是第一个节点,在解析的时候它已经不是NumberNode
//它应该是UminusNode,它里面已经能够处理负号了,应该把它当成是正的(+)-7
AppendChild(node, true);
}
void AppendChild(Node* child, bool positive)
{
childs_.push_back(child);
child_.push_back(positive);
}
~MultipleNode();
private:
//有很多子代,将其放入向量中
std::vector<Node*> childs_;
std::vector<bool> positive;//节点的正负性
};
class SumNode : public : MultipleNode
{
public:
SumNode(Node* node)
: MultipleNode(node) {}
double Calc() const;
};
//乘积节点
class ProductNode : public : MultipleNode
{
public:
SumNode(Node* node)
: MultipleNode(node) {}
double Calc() const;
};
class VariableNode: public Node
{
public:
VariableNode(unsigned int id, Storage& storage)
: id_(id), storage_(storage) {}
double Calc() const;
bool IsLvalue() const;
void Assign(double val);
private:
const unsigned int id_;
Storage& storage_;//变量节点的值从Storage类中获取,Storage类中存储了某个变量的值,所以需要Storage类的一个引用
};
class AssignNode : public BinaryNode
{
public:
//构造函数初始化,要调用基类部分的构造函数
AssignNode(Node* left, Node* right)
: BinaryNode(left, right)
{
assert(left->IsLvalue());//断言是一个左值节点,能够被赋值的
}
//要实现Calc方法
double Calc() const;
};
#endif/* _NODE_H */
P46\Node.cpp
#include "Node.h"
#include "Storage.h"
#include <cmath.h>
#include <cassert>
#include <iostream>
//数字节点的计算方法就等于数字节点本身
double NumberNode::Calc() const
{
return number_;
}
BinaryNode::~BinaryNode()
{
delete left_;
delete right_;
}
UnaryNode::~UnaryNode();
{
delete child_;
}
// double AddNode::Calc() const
// {
// //AddNode节点的值等于左计算节点得到的值+右计算节点得到的值
// return left_->Calc() + right_->Calc();
// }
// double SubNode::Calc() const
// {
// return left_->Calc() - right_->Calc();
// }
// double MultiplyNode::Calc() const
// {
// return left_->Calc() * right_->Calc();
// }
// double AddNode::Calc() const
// {
// double divisor = right_->Calc();
// if (divisor != 0.0)
// return left_->Calc() / divisor;
// else
// {
// std::cout << "Error: Divisor by zero" <<std::endl;
// return HUGE_VAL;
// }
// }
double UnaryNode::Calc() const
{
//孩子节点前面加一个负号,对照改进类继承体系的图看更好理解
return - child_->Calc();
}
MultipleNode::~MultipleNode()
{
std::vector<Node*>::const_iterator it;
for (it = children_.begin(); it != children_.end(); ++it)
{
//it实际上是Node*的指针的指针,它存放的类型是Node*,*it取出它里面存放的元素Node*
delete *it;
}
}
//把它的子节点计算一下,然后把它+起来
double SumNode::Calc() const
{
double result = 0.0;
std::vector<Node*>::const_iterator childIt = childs_.begin();
std::vector<bool>::const_iterator positiveIt = positives_.begin();
for (; childIt != childs_.end(); ++childIt, ++positive)
{
//实际上childs_和positives_的元素个数是一样的,childs_没有遍历到结尾,那么positives_也没有遍历到结尾
assert(positiveIt != positives_.begin();
double val = (*childIt)->Calc();//*childIt的类型是Node*
if (*positiveIt)
result + =val;
else
result -= val;
}
//childs_遍历到结尾,那么positives_也遍历到结尾
assert(positiveIt == positives_.end());
return result;
}
//把它的子节点计算一下,然后把它*起来
double ProductNode::Calc() const
{
double result = 1.0;
std::vector<Node*>::const_iterator childIt = childs_.begin();
std::vector<bool>::const_iterator positiveIt = positives_.begin();
for (; childIt != childs_.end(); ++childIt, ++positive)
{
//实际上childs_和positives_的元素个数是一样的,childs_没有遍历到结尾,那么positives_也没有遍历到结尾
assert(positiveIt != positives_.begin();
double val = (*childIt)->Calc();//*childIt的类型是Node*
if (*positiveIt)
result * =val;
else if (val != 0.0)
result /= val;
else
{
std::cout<<"Division by zero"<<std::endl;
return HUGE_VAL;
}
}
//childs_遍历到结尾,那么positives_也遍历到结尾
assert(positiveIt == positives_.end());
return result;
}
double VariableNode::Calc() const
{
double x = 0.0;
if (storage_.IsInit(id_))
{
x = storage_.GetValue(id_);
}
else
{
std::cout<<"Use of uninitialized variable"<<std::endl;
}
return x;
}
bool VariableNode::IsLvalue() const
{
return true;
}
void VariableNode::Assign(double val)
{
storage_.SetValue(id_, val)
}
double AssignNode::Calc() const
{
//首先取出右节点的值
double x = 0.0;
x = right->Calc();
left_->Assign(x);
}
P46\Scanner.h
#ifndef _SCANNER_H_
#define _SCANNER_H_
#include <string>
enum EToken
{
TOKEN_END;
TOKEN_ERROR;
TOKEN_NUMBER;
TOKEN_PLUS;
TOKEN_MINUS;
TOKEN_MULTIPLY;
TOKEN_DIVIDE;
TOKEN_LPARENTHESIS;
TOKEN_RPARENTHESIS;
TOKEN_IDENTIFIER;
TOKEN_ASSIGN;//eg:a=5
};
//Scanner类:只负责扫描,并且登记当前的状态
class Scanner
{
public:
Scanner(const std::string& buf);
void Accept();
double Number() const;
string GetSymbol() const;
EToken Token() const;
private:
void SkipWhite();
const std:string buf_;
unsigned int curPos_;
EToken token_;//返回状态
double number_;//返回数字
std::string symbol_;
};
#endif/*_SCANNER_H_*/
P46\Scanner.cpp
#include "Scanner.h"
#include <cctype>
Scanner::Scanner(const std::string& buf) : buf_(buf), curPos_(0)
{
Accept();//一个字符一个字符的扫描
}
double Scanner::Number() const
{
return number_;
}
string Scanner::GetSymbol() const
{
return symbol_;
}
EToken Scanner::Token() const
{
return token_;
}
//忽略空白字符
void Scanner::SkipWhite()
{
while (isspace(buf_[curPos_]))
++curPos_;
}
void Scanner::Accept()
{
SkipWhite();//首先忽略空白字符
switch (buf[curPos_])
{
case '+':
token_ = TOKEN_ADD;
++curPos_;
break;
case '-':
token_ = TOKEN_MINUS;
++curPos_;
break;
case '*':
token_ = TOKEN_MULTIPLY;
++curPos_;
break;
case '/':
token_ = TOKEN_DIVIDE;
++curPos_;
break;
case '=':
token_ = TOKEN_ASSIGN;
++curPos_;
break;
case '(':
token_ = TOKEN_LPARENTHESIS;
++curPos_;
break;
case ')':
token_ = TOKEN_RPARENTHESIS;
++curPos_;
break;
case '0': case '1': case '2' : case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.':
token_ = TOKEN_NUMBER;
char* p;//实际上这里的指针并没有指向
//buf_是一个字符串,buf_[curPos_]是一个字符,相当于得到了内部字符串的字符
//这里的指针p,指针变量p本身发送改变,也就是说它指向了其他地方,改变了指针的指向
number_ = strtod(&buf_[curPos_], &p);//返回第一个不是数字的位置
//将地址p转换为数字curPos_,用以更新curPos_
curPos_ = p - &buf[0];// &buf[0]是字符串的首地址
break;
case '\0' : case '\n' : case '\r' : case EOF://认为表达式结束了
token_ = TOKEN_END;
break;
default:
//字母或者下划线打头的才是标识符
if (isalpha(buf_[curPos_]) || buf_[curPos_] == '_')
{
token_ = TOKEN_IDENTIFIER;
symbol_.erase();
char ch = buf_[curPos_];
do
{
symbol_ += ch;
++curPos_;
ch = buf_[curPos_];
} while (isalnum(ch) || ch == '_');
}
else
{
token_ = TOKEN_ERROR;
}
break;
}
}
2.Calc类实现
-
将SymbolTable类和Storage类正常应该放到Parser类中(直接放进去应该也行),现在组合到Calc类中,SymbolTable类,Storage类与Calc类的关系是组合关系。
解析器Parser需要符号表,函数表,存储表,现在只支持符号表SymbolTable,存储表Storage
-
startUML,关系就不画了,线条太多了,麻烦
Calc类中的FindSymbol,AddSymbol,GetStorage成员函数按照Parse类中解析的需要所写的接口,也就是说,先写Parse类中对标识符的解析处理,然后再写的这里的这仨接口
P46\Calc.h
#ifndef _CALC_H_
#define _CALC_H_
#include "SymbolTable.h"
#include "Storage.h"
#include <string>
class Parser;
class Calc
{
//将Parser类声明为友元,可以方便Parser类方便访问Calc类中的私有成员
friend class Parser;
public:
Calc() : storage_(symTbl_) {}
private:
//因为这里不是引用,需要将其头文件include进来,而不能使用前向声明了
//SymbolTable类,Storage类与Calc类的关系是组合关系,其生命周期由Calc类来管理
//此外,SymbolTable对象要在Storage对象之前构造,SymbolTable对象构造完毕后传递给Storage对象
SymbolTable symTbl_;
Storage storage_;
unsigned int FindSymbol(const std::string& str) const;
unsigned int AddSymbol(const std::string& str);
Storage GetStorage()//不写成const Storage GetStorage() const的原因是:能够去更改storage_
{
return storage_;
}
};
#endif /* _CALC_H_ */
P46\Calc.cpp
#include "Calc.h"
unsigned int Calc::FindSymbol(const std::string& str) const
{
return symbol_.Find(str);
}
unsigned int Calc::AddSymbol(const std::string& str)
{
return symbol_.Add(str);
}
P46\Parser.h
#ifndef _PARSER_H
#define _PARSER_H
//使用前向声明而不是包含Scanner的头文件的原因是,如果在cpp文件中多次包含了这样的头文件,使得生成的可执行文件增大
class Scanner;
class Node;//解决在 Node* Expr();中没有定义Node
class Calc;
enum STATUS
{
STATUS_OK;
STATUS_ERROR;
STATUS_QUIT;
};
//Parser类:根据扫描结果,进行扫描,递归下降解析,直到生成一颗树
//Parser类与Scanner类之间的关系是什么?
//依赖关系:一个类作为另一个类成员函数的参数,或者内部的局部变量调用了某个类的静态成员函数
//而Scanner作为Parser类的成员,而且是引用: Scanner& scanner_;
//说明:Scanner类与Parser类具有一定的固定关系,在Parser类的生命周期内都要对scanner_的引用固定关系
//所以把他们看成关联关系
class Parser
{
public:
Parser(Scanner& scanner, Calc& calc);
void Parse();
Node* Expr();
Node* Term();
Node* Factor();
double Calculate() const;
private:
Scanner& scanner_;//这里是引用,即使Parser类销毁了,Scanner类也不不一定销毁,Parser类不负责Scanner类的生命周期
//若这里不是引用,Parser对象销毁,Scanner对象也跟着销毁,这就是组合关系了
//这里也可以用引用,但是会拷贝一个Scanner类对象拷贝到Parser类内部,组合的方式效率低一些,没必要用了
Node* tree_;
STATUS status_;
Calc& calc_;
};
#endif /* _PARSER_H */
P46\Parser.cpp
#include "Parser.h"
#include "Scanner.h"//因为会使用到Scanner的一些接口进行扫描
#include "Node.h"
#include "Calc.h"
#include <cassert>
#include <iostream>
//引用的初始化只能才初始化列表中进行初始化
Parser::Parser(Scanner& scanner, Calc& calc) : scanner_(scanner), tree_(0), calc_(calc)
{
}
//解析表达式:
Node* Parser::Expr()
{
Node* node = Term();
EToken token = scanner_.Token();
// if (token == TOKEN_PLUS)//扫描到+
// {
// scanner_.Accept();//accept+号,扫描下一个字符,看看是不是一个Expression
// Node* nodeRight = Expr();
// node = new AddNode(node, nodeRight);//返回的是加法节点,(左节点,右节点),Expression is Term + Expression
// }
// else if (token == TOKEN_MINUS)
// {
// scanner_.Accept();
// Node* nodeRight = Expr();
// node = new SubNode(node, nodeRight);//Expression is Term - Expression
// }
if (token == TOKEN_PLUS || token == TOKEN_MINUS)
{
//此时的表达式,Expr := Term{ ( '+'| '-' ) Term}
MultipleNode* multipleNode = New SumNode(node);
//子节点有多个,所以用循环
do
{
scanner_.Accept();
Node* nextNode = Term();
multipleNode->AppendChild(nextNode, (token == TOKEN_PLUS));
token = scanner_.Token();//更新当前扫描的状态,即:看下一个字符是加法还是减法,直到遇到其他负号,就不是SumNode的子代了
}while(token == TOKEN_PLUS || token == TOKEN_MINUS)
node = multipleNode;
}
return node;//Expression is Term
}
//解析项
Node* Parser::Term()
{
Node* node = Factor();
EToken token = scanner_.Token();
// if (token == TOKEN_MULTIPLY)
// {
// scanner_.Accept();
// Node* nodeRight = Term();
// node = new MultiplyNode(node, nodeRight);//Term is Factory * Term
// }
// else if (token == TOKEN_DIVIDE)
// {
// scanner_.Accept();
// Node* nodeRight = Term();
// node = new DivideNode(node, nodeRight);//Term is Factory / Term
// }
if (token == TOKEN_MULTIPLY || token == TOKEN_DIVIDE)
{
//此时的表达式,Expr := Factor{ ( '*'| '/' ) Factor}
MultipleNode* multipleNode = New SumNode(node);
//子节点有多个,所以用循环
do
{
scanner_.Accept();
Node* nextNode = Factor();
multipleNode->AppendChild(nextNode, (token == TOKEN_MULTIPLY));
token = scanner_.Token();//更新当前扫描的状态,即:看下一个字符是加法还是减法,直到遇到其他负号,就不是SumNode的子代了
}while(token == TOKEN_MULTIPLY || token == TOKEN_DIVIDE)
node = multipleNode;
}
else if (token == TOKEN_ASSIGN) //项还能够解析等号,比如x=5,x是项,5是表达式;x=y=5,x是项,y=5是表达式
{
//Expr:=Term=Expr, :=这是一种BNF式的表达法
scanner_.Accept();
Node* nodeRight = Expr();
if (node->IsLvalue())
{
node = new AssignNode(node, nodeRight);//这里是对创建的变量节点,进行的赋值操作
}
else
{
status_ = STATUS_ERROR;
std::cout<<"The left-hand side of an assignment must be a variable"<<std::endl;//必须是左值节点
//Todo 抛出异常
}
}
return node;//Expression is Factory
}
//解析因式
Node* Parser::Factor()
{
//or (Expression)
Node* node = 0;
EToken token = scanner_.Token();
if (token == TOKEN_LPARENTHESIS)
{
scanner_.Accept(); //accept '('
node = Expr();//先解析表达式,右边应该有个右括号
if (scanner_ == TOKEN_RPARENTHESIS)
{
scanner_.Accept(); //accept ')'
}
else
{
status = STATUS_ERROR;
//to do:抛出异常
std::cout<<"missing parenthesis"<<std::endl;
node = 0;
}
else if (token == STATUS_NUMBER)
{
node = new NumberNode(scanner_.Number());//新建一个数字节点
scanner_.Accept();
}
else if(token == STATUS_MINUS)
{
scanner_.Accept();//接受一个负号,目的是指针偏移到下一个字符,让下一次的解析做准备
node = new UminusNode(Factor());//传递一个子节点进去,这个子节点就是因式
}
else if (token == TOKEN_IDENTIFIER)//解析到一个标识符
{
std::string symbol = scanner_.GetSymbol();
unsigned int id = calc_.FindSymbol(symbol);
scanner_.Accept();
if (id == SymbolTable::IDNOTFOUND)
{
id = calc_.AddSymbol(symbol);
}
node = new VariableNode(id, calc_.GetStorage());//创建一个变量节点
}
else
{
status = STATUS_ERROR;
//to do:抛出异常
std::cout<<"Not a valid expression"<<std::endl;
node = 0;
}
return node;
}
}
void Parser::Parse()
{
tree_ = Expr();//解析完后,将整个表达式赋值给tree_
}
//注意:带const的成员函数与不带const的成员函数可以构成重载
double Parser::Calculate() const
{
assert(tree_ != 0);//0,NULL都行
//求表达式的值,实际上就是求其根节点的值
return tree_->Calc();
}
P46\main.cpp
#include <iostream>
#include <string>
#include "Scanner.h"
#include "Parser.h"
#include "Calc.h"
int main(void)
{
Calc calc;
STATUS status= STATUS_OK;
do
{
std::cout<<">";
std::string buffer;
std::getline(std::cin, buffer);//输入一行表达式放到buf当中
// std::cout<<buffer<<std::endl;
Scanner scanner(buffer);
Parser parser(scanner, calc);
parser.Parse();//实际上计算表达式的值,就是计算这颗树的根节点的值
std::cout<<paese.Calculate()<<std::endl;
}while(status != STATUS_QUIT);
return 0;
}
没变化的有:
P46\SymbolTable.h
#ifndef _SYMBOL_TABLE_H_
#define _SYMBOL_TABLE_H_
#include <map>
#include <string>
class SymbolTable
{
//直接放在public上面是私有的
public:
//枚举量
enum { INNOTFOUND = 0xffffffff }
SymbolTable() : curId_(0) {}
unsigned int Add(const std::string& str);
unsigned int Find(const std::string& str) const;
void Clear();
std::string GetSymbolName(unsigned int id) const;
private:
std::map<std::string, unsigned int> dictionary_;
unsigned int curId_;
};
#endif /* _SYMBOL_TABLE_H_ */
P46\SymbolTable.cpp
#include "SymbolTable.h"
unsigned int SymbolTable::Add(const std::string& str)
{
dictionary_[str] = curId_;
return curId_++;//返回值是当前符号的id,然后再++
}
unsigned int SymbolTable::Find(const std::string& str) const
{
map<const std::string, unsigned int>::const_iterator it;
it = dictionary_.find(str);
if (it != dictionary_.end() )
return it->second;
return INNOTFOUND;
}
//不等于end,说明找到了
void SymbolTable::Clear()
{
dictionary_.clear();
curId_ = 0;
}
//函数对象function object,反函数functor
//目的:让一个类对象使用起来像一个函数,本质上是重载括号运算符
/*
IsEqualId ie;
is();此时ie使用起来像一个函数
*/
//这是STL6大组件
//迭代器pair有2个要素:fist是string,second是unsigned int
class IsEqualId
{
public:
IsEqualId(unsigned int id) : id_(id) {}
bool operator(const std::pair<std::string,unsigned int>& it) const
{
return it.second == id_;
}
private:
unsigned int id_;
};
std::string SymbolTable::GetSymbolName(unsigned int id) const
{
//find_if(XX,XX,类)
//将类传递进去,将类看成是一个函数
map<const std::string, unsigned>::const_iterator it;
it = find_if(dictionary_begin(), dictionary_end(), IsEqualId(id));
return it->first;
}
P46\Storage.h
#ifndef _STORAGE_H
#define _STORAGE_H
#include <vector>
class SymbolTable;//这里是前向声明,需要在cpp文件中包含#include "SymbolTable.h"
class Storage
{
public:
Storage(SymbolTable& tbl);
void Clear();
bool IsInit(unsigned int id) const;
void AddConstant(SymbolTable& tbl);
double GetValue(unsigned int id) const;
void SetValue(unsigned int id, double val);
void AddValue(unsigned int id, double val);
private:
std::vector<double> cells_;
std::vector<bool> inits_;
};
#endif /* _STORAGE_H */
P46\Storage.cpp
#include "Storage.h"
#include "SymbolTable.h"
#include <cmath>
#include <cassert>
Storage::Storage(SymbolTable& tbl)
{
//先添加常量到符号表中
AddConstant(tbl);
}
void Storage::Clear()
{
cells_.clear();
inits_.clear();
}
bool Storage::IsInit(unsigned int id) const
{
//id不能超过cells_元素中的最大值
//下标就是id,第0变量是否初始化了,那么就是inits_[0]
return id < cells.size() && inits_[id];
}
//添加2个常量
void Storage::AddConstant(SymbolTable& tbl)
{
unsigned int id = tbl.Add("e");
AddValue(id, exp(1.0));//exp(1.0):代表e的1一次方
id = tbl.Add("pi");
AddValue(id, 2.0*acos(0.0));//pi=2*acos(0),acos是反余弦函数
}
double Storage::GetValue(unsigned int id) const
{
assert(id < cells_.size());
return cells_[id];
}
void Storage::SetValue(unsigned int id, double val)
{
assert(id <= cells_.size());
if (id < cells_.size())//id已经存在重新赋值
{
cells_[id] = val;
inits_[id] = true;
}
else if (id == cells_.size())//id超过size,也就是说id所对应的变量不存在
{
AddValue(id, value);
}
}
void Storage::AddValue(unsigned int id, double val)
{
//为啥不使用push_back?
/*
比如cells_空间里面有6个元素了,此时cells_.push_back(val);此时向量里面的空间并不等于7,有可能是12,
会导致size为12,实际上并没存放12个变量或常量,我们希望向量cells_存放多少变量或常量,其空间就为多少,即size就为
多少,所以不用
*/
//先扩充一个容量
cells_.resize(id+1);
inits_.resize(id+1);
cells_[id] = value;
inits_[id] = true;
}
- 测试: