C++ Primer 15.9的实践内容
因为不管是习题集还是书附赠的源码,都没有很好实现一个支持输入表达式的查询效果,因为这一节算是对整本书第三部分的一个总结和实践,所以有必要实现一个效果比较好的,符合书中预期效果的程序
书中附带源码的程序其实已经实现了支撑C++表达式的单词查询,但是没有做将输入字符串作为表达式转化为C++表达式的部分,所以我做的工作相当于给原程序增加了直接从命令行输入表达式再转化为C++表达式对字符进行查询的部分
原程序的C++表达式本身的完成度已经很高了。将命令行的字符串转化,其实就是类似实现一个简易计算器,将命令行输入的中缀表达式转化为后缀表达式,再使用栈结构运算即可。
简易计算器的部分可以看这篇博客
程序源码:
#include <iostream>
#include <fstream>
#include <stack>
#include "WordQuery.hh"
#include <readline/readline.h>
#include <readline/history.h>
using namespace std;
void Execute(TextQuery&);
string Transform(const string&);
Query calculate(const string&);
int weight(const char);
void calculate_operater(const string&, stack<Query>&);
bool Error(false);
int main(int argc, char* argv[])
{
if(argc != 2)
{
cout << "Error format!" << endl;
exit(1);
}
ifstream in(argv[1]);
if(!in)
{
cout << "Open file error!" << endl;
exit(1);
}
TextQuery TQ(in);
Execute(TQ);
return 0;
}
void Execute(TextQuery& TQ)
{
string Aline;
//Query res;
using_history();
while(true)
{
Aline = readline("enter experssion, or q to quit: ");
Aline = Transform(Aline);
if(Error)
{
cout << "invalid charactor or syntax error!" << endl;
add_history(Aline.c_str());
Error = false;
continue;
}
if(Aline == "")
{
continue;
}
Query res(calculate(Aline));
if(Error)
{
cout << "invalid charactor or syntax error!" << endl;
add_history(Aline.c_str());
Error = false;
continue;
}
print(cout, res.eval(TQ)) << endl;
}
return;
}
string Transform(const string& line)
{
if(line == "q")
{
exit(1);
}
add_history(line.c_str());
stack<char> Stack_charactors;
string ret;
string num;
string Aline;
bool flag(true);
Aline += line + "#";
//cout << Aline << endl;
//解析字符串
for(auto itc = Aline.cbegin(),
ite = Aline.cend(); ite!=itc; ++itc)
{
//空格直接跳过
if(*itc == ' ')
{
continue;
}
//如果是单词
else if((*itc >= 'a' && *itc <= 'z') || (*itc >= 'A' && *itc <= 'Z'))
{
num += *itc;
flag = true;
}
else
{
if(weight(*itc) == -1)
{
Error = true;
return "";
}
if(flag == true)
{
ret = ret + num + " ";
num = "";
}
if(Stack_charactors.empty() || (*itc == '('))
{
Stack_charactors.push(*itc);
}
else if(*itc == ')')
{
while(Stack_charactors.top() != '(')
{
ret = ret + Stack_charactors.top() + " ";
Stack_charactors.pop();
if(Stack_charactors.empty())
{
Error = true;
return "";
}
}
Stack_charactors.pop();
}
else
{
while((!Stack_charactors.empty()) && (weight(*itc) <= weight(Stack_charactors.top())))
{
ret = ret + Stack_charactors.top() + " ";
Stack_charactors.pop();
}
Stack_charactors.push(*itc);
}
flag = false;
}
}
ret.resize(ret.size()-1);
return ret;
}
int weight(const char sign)
{
switch(sign)
{
case '#':
{
return 0;
}
case '|':
{
return 1;
}
case '&':
{
return 1;
}
case '~':
{
return 1;
}
case '(':
{
return 0;
}
case ')':
{
return 0;
}
default:
{
return -1;
}
}
}
Query calculate(const string& expression)
{
int ret;
istringstream is(expression);
string item;
stack<Query> Stack_nums;
while(is >> item)
{
if((item=="|") || (item=="&") || (item=="~"))
{
calculate_operater(item, Stack_nums);
if(Error)
{
return Query("");
}
}
else
{
Stack_nums.push(Query(item));
}
}
return Stack_nums.top();
}
void calculate_operater(const string& sign, stack<Query>& Stack_nums)
{
//Query t1, t2;
if(sign == "|")
{
if(Stack_nums.size() < 2)
{
Error = true;
return;
}
Query t2(Stack_nums.top());
Stack_nums.pop();
Query t1(Stack_nums.top());
Stack_nums.pop();
Stack_nums.push(t1 | t2);
}
else if(sign == "&")
{
if(Stack_nums.size() < 2)
{
Error = true;
return;
}
Query t2(Stack_nums.top());
Stack_nums.pop();
Query t1(Stack_nums.top());
Stack_nums.pop();
Stack_nums.push(t1 & t2);
}
else if(sign == "~")
{
if(Stack_nums.size() < 1)
{
Error = true;
return;
}
Query t1(Stack_nums.top());
Stack_nums.pop();
Stack_nums.push(~t1);
}
return;
}
头文件源码:
//WordQuery.hh
#ifndef _WORDQUERY_
#define _WORDQUERY_
#include "/home/tyf/my-code_cpp/TextQuery/TextQuery.hh"
#include <iostream>
#include <string>
#include <memory>
#include <set>
#include <iterator>
#include <algorithm>
class QueryBase {
friend class Query;
protected:
virtual ~QueryBase() {
}
using line_no = TextQuery::line_no;
private:
virtual QueryResult eval(const TextQuery&) const = 0;
virtual std::string rep() const = 0;
};
class Query {
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
Query(const std::string&);
QueryResult eval(const TextQuery& t) const {
return q->eval(t); };
std::string rep() const {
return q->rep(); }
private:
Query(std::shared_ptr<QueryBase> query): q(query) {
}
std::shared_ptr<QueryBase> q;
};
inline
std::ostream& operator<<(std::ostream& os, const Query& query)
{
return os << query.rep();
}
class WordQuery: public QueryBase {
friend class Query;
WordQuery(const std::string& word): query_word(word) {
}
QueryResult eval(const TextQuery& t) const {
return t.query(query_word); }
std::string rep() const {
return query_word; }
std::string query_word;
};
inline
Query::Query(const std::string& word): q(new WordQuery(word)) {
}
class NotQuery: public QueryBase {
friend Query operator~(const Query&);
NotQuery(const Query& q): query(q) {
}
QueryResult eval(const TextQuery& t) const;
std::string rep() const {
return "~(" + query.rep() + ")"; }
Query query;
};
class BinQuery: public QueryBase {
protected:
BinQuery(const Query& l, const Query& r, const std::string sign): lhs(l), rhs(r), sign(sign) {
}
std::string rep() const {
return "(" + lhs.rep() + " " + sign + " " + rhs.rep() + ")"; }
Query lhs, rhs;
std::string sign;
};
class AndQuery: public BinQuery {
friend Query operator&(const Query&, const Query&);
AndQuery(const Query& l, const Query& r): BinQuery(l, r, "&") {
}
QueryResult eval(const TextQuery&) const;
};
class OrQuery: public BinQuery {
friend Query operator|(const Query&, const Query&);
OrQuery(const Query& l, const Query& r): BinQuery(l, r, "|") {
}
QueryResult eval(const TextQuery&) const;
};
inline
Query operator&(const Query& lhs, const Query& rhs)
{
return std::shared_ptr<QueryBase>(new AndQuery(lhs, rhs));
}
inline
Query operator|(const Query& lhs, const Query& rhs)
{
return std::shared_ptr<QueryBase>(new OrQuery(lhs, rhs));
}
inline
Query operator~(const Query& nq)
{
return std::shared_ptr<QueryBase>(new NotQuery(nq));
}
QueryResult NotQuery::eval(const TextQuery& TQ) const
{
QueryResult ret = query.eval(TQ);
std::shared_ptr<std::set<line_no>> ret_SetPtr(new std::set<line_no>);
std::set<line_no>::const_iterator itc = ret.begin(), ite = ret.end();
std::vector<std::string>::size_type sz = ret.get_file()->size();
//这段循环匹配算法挺有意思的
for(std::size_t n=0; n!=sz; ++n)
{
if(itc == ite || *itc != n)
{
ret_SetPtr->insert(n);
}
else if(itc != ite)
{
++itc;
}
}
return QueryResult(rep(), ret.get_file(), ret_SetPtr);
}
QueryResult AndQuery::eval(const TextQuery& TQ) const
{
QueryResult left = lhs.eval(TQ), right = rhs.eval(TQ);
std::shared_ptr<std::set<line_no>> ret_SetPtr(new std::set<line_no>);
//对这个算法和这个迭代器容器的了解不深
std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), std::inserter(*ret_SetPtr, ret_SetPtr->begin()));
return QueryResult(rep(), left.get_file(), ret_SetPtr);
}
QueryResult OrQuery::eval(const TextQuery& TQ) const
{
QueryResult left = lhs.eval(TQ), right = rhs.eval(TQ);
std::shared_ptr<std::set<line_no>> ret_SetPtr(new std::set<line_no>(left.begin(), left.end()));
ret_SetPtr->insert(right.begin(), right.end());
return QueryResult(rep(), left.get_file(), ret_SetPtr);
}
#endif
//TextQuery.hh
#ifndef _TEXTQUERY_
#define _TEXTQUERY_
#include <iostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <vector>
#include <map>
#include <set>
#include <ctype.h>
class QueryResult;
std::ostream& print(std::ostream&, const QueryResult&);
class TextQuery {
friend class QueryResult;
public:
using line_no = std::vector<std::string>::size_type;
TextQuery(std::ifstream&);
QueryResult query(const std::string&) const;
private:
std::shared_ptr<std::vector<std::string>> VecPtr;
std::map<std::string, std::shared_ptr<std::set<line_no>>> Map;
static std::string cleanup_str(const std::string& word);
};
class QueryResult {
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
using line_no = std::vector<std::string>::size_type;
QueryResult(std::string Word,
std::shared_ptr<std::vector<std::string>> VecPtr,
std::shared_ptr<std::set<line_no>> SetPtr):
Word(Word), VecPtr(VecPtr), SetPtr(SetPtr) {
}
std::set<line_no>::size_type size() const {
return SetPtr->size(); }
std::set<line_no>::const_iterator begin() const {
return SetPtr->begin(); }
std::set<line_no>::const_iterator end() const {
return SetPtr->end(); }
std::shared_ptr<std::vector<std::string>> get_file() {
return VecPtr; }
private:
std::string Word;
std::shared_ptr<std::vector<std::string>> VecPtr;
std::shared_ptr<std::set<line_no>> SetPtr;
};
inline
TextQuery::TextQuery(std::ifstream& infile): VecPtr(new std::vector<std::string>)
{
std::string Aline;
std::string Aword;
line_no n;
while(getline(infile, Aline))
{
VecPtr->push_back(Aline);
n = VecPtr->size() - 1;
std::istringstream Wordstream(Aline);
while(Wordstream >> Aword)
{
Aword = cleanup_str(Aword);
auto& SetPtr = Map[Aword];
if(!SetPtr)
{
SetPtr.reset(new std::set<line_no>);
}
SetPtr->insert(n);
}
}
}
inline
std::string TextQuery::cleanup_str(const std::string& word)
{
std::string ret;
for(std::string::const_iterator it = word.cbegin(); it!=word.cend(); ++it)
{
//如果不是标点符号
if(!ispunct(*it))
{
//转换小写
ret += tolower(*it);
}
}
return ret;
}
inline
QueryResult TextQuery::query(const std::string& word) const
{
//为什么是static
static std::shared_ptr<std::set<line_no>> nodata(new std::set<line_no>);
std::map<std::string, std::shared_ptr<std::set<line_no>>>::const_iterator it = Map.find(cleanup_str(word));
//it是一个pair类
if(it == Map.cend())
{
return QueryResult(word, VecPtr, nodata);
}
else
{
return QueryResult(word, VecPtr, it->second);
}
}
inline
std::string make_plural(std::size_t ctr, const std::string &word, const std::string &ending)
{
return (ctr > 1) ? word + ending : word;
}
inline
std::ostream& print(std::ostream& os, const QueryResult& result)
{
os << result.Word << " occurs " << result.SetPtr->size() << " " << make_plural(result.SetPtr->size(), "time", "s") << std::endl;
for(std::set<TextQuery::line_no>::const_iterator it=result.SetPtr->cbegin(); it!=result.SetPtr->cend(); ++it)
{
os << "\t(line " << *it + 1 << ") " << *(result.VecPtr->cbegin() + *it) << std::endl;
}
return os;
}
#endif