文本单词查询复合表达式求值的实现案例分析

        本文讨论的“文本单词查询复合表达式求值的实现”案例,来自C++ primer第四版,该案例面向对象编程和泛型编程,涉及类的继承、抽象、多态、句柄、标准IO库、容器、算法库,是综合性很强的程序

        该程序实现文本中查找单个单词,“非”查询(使用~操作符),“或”查询(使用|操作符),“与”查询(使用&操作符),组合查询(如fiery & bird | wind),查询表达式求值 并 打印输出查询结果:符合查询条件的文本共多少行、在查询本文的第几行、以及文本内容,查询使用文本如下:

Alice Emma has long flowing red hair. 
Her Daddy says when the wind blows 
through her hair, it looks almost alive, 
like a fiery bird in flight. 
A beautiful fiery bird, he tells her, 
magical but untamed. 
"Daddy, shush, there is no such thing," 
she tells him, at the same time wanting 
him to tell her more.
Shyly, she asks, "I mean, Daddy, is there?"   

       一、程序架构

        创建标准IO库的ifstream对象infile,定义的辅助函数openfile在命令行模式下,用对象infile打开文本文件,具体操作是:在命令行下输入程序名字及文本文件所在目录

        TextQuery类实现文本的读取存储,容器vector按行存储文本,容器map存储所有单词所在的行的set容器,接口函数run_query返回一个单词所在的行的set容器,text_line返回具体行的文本,size返回存储的文本共多少行

        句柄类Query,包装类型为查询基类Query_base的模板句柄实例对象,该句柄实例包装查询基类指针,Query类对象由查询基类指针构造或string字符串(由字符串构造的对象返回的是基类指针)构造,Query类对象的构造,最终初始化查询基类指针注意:查询基类指针指向具体的查询对象:继承自查询基类Query_base的子类WordQuery、NotQuery、BinaryQuery(BinaryQuery的子类AndQuery、OrQuery)的对象

        句柄类的友元函数,是重载 |、&、~操作符的函数,返回句柄类对象(动态构造的具体查询对象返回的是查询基类指针,查询基类指针可以隐式转换为句柄类对象),用以实现查询表达式

        句柄类Query的接口函数display显式具体查询表达式,接口函数eval返回符合查询条件的set容器(存储所有符合查询条件的文本行)

        程序几个版本的演变及完整代码,请点击这里(本文讨论的是第六版)

        二、查询基类Query_base及其子类WordQuery、NotQuery、BinaryQuery(BinaryQuery的子类AndQuery、OrQuery)

        查询基类Query_base代码:

class Query_base{friend class Query; //句柄Queryfriend class Handle<Query_base >;//类型为Query_base的句柄模板实例
protected://派生类访问typedef TextQuery::line_no line_no;virtual ~Query_base() {  }
private:   //两个接口,用户和Query_base类的派生类只通过句柄Query使用Query_base类virtual std::set<line_no> eval(const TextQuery&) const = 0;virtual std::ostream& display(std::ostream& = std::cout) const = 0;
};

        查询基类Query_base的子类WordQuery类的代码:

class WordQuery: public Query_base{friend class Query;//句柄Query友元friend class Handle<Query_base >;WordQuery(const std::string &s ):query_word(s ) {  }std::set<line_no > eval(const TextQuery &t)const{  //实现基类纯虚函数evalreturn t.run_query(query_word ); //用到了TextQuery类的成员函数run_query}std::ostream& display(std::ostream &os)const{      //实现基类纯虚函数displayreturn os << query_word;}std::string query_word;  //数据成员query_word
};

        查询基类Query_base的子类NotQuery类的代码:

class NotQuery: public Query_base{friend Query operator~(const Query & );NotQuery(Query q ):query(q ) {  } //构造std::set<line_no > eval(const TextQuery & )const;std::ostream& display(std::ostream &os )const{return os << "~(" << query << ")"; //输出操作符的使用最终是对Query_base对象的虚函数的调用}const Query query;  //数据成员Query句柄对象
};
std::set<TextQuery::line_no > NotQuery::eval(const TextQuery &file) const{std::set<TextQuery::line_no > has_val = query.eval(file );std::set<line_no > ret_lines;for(TextQuery::line_no n = 0; n != file.size(); ++n ){    if(has_val.find(n) == has_val.end() ) //没找到,就插入ret_lines.insert(n);}return ret_lines;
}    

        查询基类Query_base的子类BinaryQuery类的代码:

class BinaryQuery: public Query_base{
protected:BinaryQuery(Query left, Query right, std::string op ):lhs(left),rhs(right),oper(op) {  }std::ostream& display(std::ostream &os)const{return os << "(" << lhs << " " << oper << " "<< rhs << ")"; //输出操作符的使用最终是对Query_base对象的虚函数的调用}const Query lhs, rhs;const std::string oper;
};

        查询基类Query_base的子类BinaryQuery类的子类AndQuery类的代码:

class AndQuery: public BinaryQuery{friend Query operator&(const Query &, const Query & );AndQuery(Query left, Query right ):BinaryQuery(left, right, "&" ) {  }std::set<line_no > eval(const TextQuery & )const;
};
std::set<TextQuery::line_no > AndQuery::eval(const TextQuery &file) const{std::set<line_no > left = lhs.eval(file ),right = rhs.eval(file );std::set<line_no > ret_lines;set_intersection(left.begin(), left.end(),right.begin(),right.end(),inserter(ret_lines, ret_lines.begin()) );return ret_lines;
}

        算法std::set_intersection,构造一个排序范围,它是两个排序范围的集合交集,实现&运算

        查询基类Query_base的子类BinaryQuery类的子类OrQuery类的代码:

class OrQuery: public BinaryQuery{friend Query operator|(const Query &, const Query & );OrQuery(Query left, Query right ):BinaryQuery(left, right, "|" ) {  }std::set<line_no > eval(const TextQuery & )const;
};
std::set<TextQuery::line_no > OrQuery::eval(const TextQuery &file) const{std::set<line_no > right = rhs.eval(file ),ret_lines = lhs.eval(file );    //返回的set对象ret_lines初始化为lhs的结果ret_lines.insert(right.begin(), right.end() );return ret_lines;
}

        三、句柄模板

        句柄简单的来讲,它是一个类,包装了一种基类指针的一个类,该指针指向动态创建的有继承关系的对象,句柄确保指针指向的对象在有指针指向的时候不会释放,在没有指针指向的时候释放,句柄避免了对象的重复创建,句柄在C++沉思录一书中有详细的描述,可以点击这里查看

        句柄模板类Handle代码:

template<class T> class Handle{
private:T* ptr;          //指向基础对象的指针size_t *use;     //指针计数 //即多少个指针指向同一个基础对象void rem_ref(){  //删除基础对象(根据计数判断是否删除)if(--*use == 0){delete ptr; //删除基础对象delete use; //删除计数}}
public:Handle(T *p = 0 ):ptr(p ),use(new size_t(1) ){ } //指针ptr指向动态分配的基础对象的地址//复制控制Handle(const Handle& h):ptr(h.ptr ), use(h.use) { ++*use; } //复制,++*useHandle& operator=(const Handle& rhs );~Handle() { rem_ref(); }   //Handle对象析构 // 删除基础对象(根据计数判断是否删除)//用于访问基础对象const T& operator*()const; //解引用操作符const T* operator->()const; //成员操作符            
};template<class T>
inline Handle<T>& Handle<T>:: operator=(const Handle& rhs)
{++*rhs.use;     //protect against self-assignment //防止自我复制rem_ref();    //decrement use count and delete pointers if neededptr = rhs.ptr;use = rhs.use;return *this;
}template<class T>//const Handle对象可以调用,返回类型是const T&不可以修改基础对象
inline const T& Handle<T>::operator*()const   
{if(ptr)return *ptr;throw std::runtime_error("dereference of unbound Handle");
}template<class T>
inline  const T* Handle<T>::operator->()const
{if(ptr)return ptr;throw std::runtime_error("dereference of unbound Handle");
} 

        四、句柄类Query(包装句柄模板实例对象 Handle<Query_base > h)的代码

class Query{  //使用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 &); //!!!  std::set<TextQuery::line_no> eval(const TextQuery &t)const {return h->eval(t); } //!!!std::ostream &display(std::ostream &os = std::cout) const {return h->display(os); }//!!!
private:Query(Query_base *query):h(query) {  } //!!!操作符创建Query对象调用Handle<Query_base > h; //!!!
};
Query::Query(const std::string &s ):h(new WordQuery(s ) ) {   }    ; //!!!创建WordQuery对象
inline std::ostream& operator << (std::ostream &os, const Query &q){return q.display(os ); //参数q是const,调用的display函数也必须是const
}//return语句隐式调用接受Query_base指针的Query构造函数//隐式类型转换
inline Query operator&(const Query &lhs, const Query &rhs){return new AndQuery(lhs, rhs );
}
inline Query operator|(const Query &lhs, const Query &rhs){return new OrQuery(lhs, rhs );
}
inline Query operator~(const Query &oper){return new NotQuery(oper );
}      

        五、查询结果打印函数

void print_results(const std::set<TextQuery::line_no > &locs, const std::string &sought,const TextQuery &file  )
{typedef std::set<TextQuery::line_no > line_nums;line_nums::size_type size = locs.size();std::cout << "\n" << sought << " occurs "<< size << " "<< make_plural(size, "time", "s" ) << std::endl;line_nums::const_iterator it = locs.begin();for( ; it != locs.end(); ++it ){std::cout << "\t(line "<< (*it) + 1 << ")"<< file.text_line(*it ) << std::endl;}
}std::string make_plural(std::size_t ctr, const std::string &word,const std::string &ending )    //make_plural //单词复数形式
{return (ctr == 1 ) ? word : word + ending;
}

        六、文件打开函数open_file

std::ifstream& open_file(std::ifstream &in, const std::string &file )
{in.close(); //如果已经打开in.clear();in.open(file.c_str() ); //打开文件return in;
}

        七、文本存储TextQuery类

class TextQuery{
public:typedef std::vector<std::string>::size_type line_no;void read_file(std::ifstream &is){  //std::ifstream &store_file(is);build_map();}std::set<line_no> run_query(const std::string & )const;std::string text_line(line_no )const;line_no size() const{ return lines_of_text.size(); } //!!!const函数
private:void store_file(std::ifstream & );  //std::ifstream & //保存文件void build_map();                   //从文件提取单词(去除标点符号)并记录单词所在文件的行号   std::vector<std::string> lines_of_text;  std::map< std::string, std::set<line_no> > word_map;//
};void TextQuery::store_file(std::ifstream &is )
{std::string textline;while(getline(is,textline) ){lines_of_text.push_back(textline );}
}void TextQuery::build_map()
{for( line_no line_num = 0; line_num != lines_of_text.size();++line_num ){std::istringstream line(lines_of_text[line_num] );//从std::vector读到 std::istringstreamstd::string word;        while(line >> word ){word.std::string::erase( std::remove_if(word.begin(),word.end(), static_cast<int(*)(int) >(&ispunct)), word.end() );//!!!去除标点符号word_map[word].insert(line_num );}}    
}std::set<TextQuery::line_no> TextQuery::run_query(const std::string &query_word )const
{std::map<std::string,std::set<line_no> > ::const_iterator loc = word_map.find(query_word ); //std::map里查找行std::setif(loc == word_map.end() )return std::set<line_no>();  //没找到返回set空对象elsereturn loc->second;
}std::string TextQuery::text_line(line_no line ) const
{if(line <lines_of_text.size() )return lines_of_text[line ];throw std::out_of_range("line number out of range");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/616510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ELAU MC-4/11/22/400伺服驱动器

在一帧中每一行的选择时间是均等的。假设一帧的扫描行数为N&#xff0c;扫描时间为1&#xff0c;那一行所占有的选择时间为一帧时间的1/N。在液晶显示的驱动方法中把这个值&#xff0c;即一帧行扫描数的倒数称为液晶显示驱动的占空比(duty)&#xff0c;用d表示。在同等电压下&a…

Error: start of central directory not found; zipfile corrupt.

【报错】使用 unzip 指令在 AutoDL 上解压 .zip 文件时遇到 Error: start of central directory not found; zipfile corrupt. 报错&#xff1a; 重新上传后还是解压失败排除了 .zip 文件上传中断的问题。 【原因】Windows 和 Linux 下的压缩文件的二进制格式有所不同&#x…

Python的内置函数 def __init__和__str__用法

__init__() 当使用类名&#xff08;&#xff09;创建对象时&#xff0c;会自动执行以下操作 __init__()是对象的的内置方法&#xff0c;是专门用来定义一个类 具有哪些属性的方法 class Person:def __init__(self):print("这是一个初始化方法")result Person() …

SpringBoot使用过滤器进行接口签名防参数篡改

在Spring Boot中&#xff0c;可以使用过滤器&#xff08;Filter&#xff09;来实现接口签名验签。以下是一个简单的示例&#xff1a; 1. 首先&#xff0c;创建一个名为SignatureFilter的类&#xff0c;实现javax.servlet.Filter接口 import javax.servlet.*; import javax.se…

直接写一区! ZOA-PCNN-AT-SVM斑马优化并行卷积-支持向量机融合注意力机制的故障识别程序,特征可视化,实验多!图多!

适用平台&#xff1a;Matlab2023版本及以上 本原创程序提出的ZOA-PCNN-AT-SVM故障识别模型还没有人写&#xff01;在此基础上进一步对参考模型进行多重改进&#xff0c;程序注释清晰&#xff0c;干货满满&#xff0c;下面对文章和程序做简要介绍&#xff01; ①识别模型部分参…

模拟开关灯

1&#xff0e;  实验任务 如图所示&#xff0c;监视开关K1&#xff08;接在P3.0端口上&#xff09;&#xff0c;用发光二极管L1&#xff08;接在单片机P1.0端口上&#xff09;显示开关状态&#xff0c;如果开关合上&#xff0c;L1亮&#xff0c;开关打开&#xff0c;L1熄灭。…

【mybatis-generator】mybatis代码生成器generator,生成文件名自定义配置

mybatis代码生成器generator, 生成文件名自定义配置 MyBatis-Generator 使用一. 引入依赖和相关插件二.设置配置文件三.运行四. mybatis-generator 自定义生成的文件名一.domainObjectName和mapperName属性二.domainObjectRenamingRule标签三. RenameExampleClassPlugin插件四.…

JUC之Phaser的使用

Phaser是并发包juc.concurrent包下的一个关于线程同步和线程通信的一个工具类&#xff0c;类似于CountDownLanch 和 CyclicBarrier&#xff0c;不同的是 Phaser可以用来根据步骤&#xff0c;等待线程按步骤同时触发执行。 package com.example.test;import com.example.abstra…

ubuntu20.04网络问题以及解决方案

1.网络图标消失&#xff0c;wired消失&#xff0c;ens33消失 参考&#xff1a;https://blog.51cto.com/u_204222/2465609 https://blog.csdn.net/qq_42265170/article/details/123640669 原始是在虚拟机中切换网络连接方式&#xff08;桥接和NAT&#xff09;&#xff0c; 解决…

2024年1月6日~2024年1月12日周报

目录 一、前言 二、SeisInvNet-2020 三、RTM研究 四、遇到的问题及解决 4.1 KeyError: data 4.2 将mat文件转换为npy文件 五、小结 5.1 存在的问题及疑惑 5.2 下周安排 一、前言 本周的主要安排是阅读论文查看一些好的点子。 但是想法总是美好的&#xff0c;之前答应的…

图灵机:计算机科学的奠基之作

图灵机的概念由英国数学家阿兰图灵在1936年提出&#xff0c;这个时期正是计算机科学的黎明时分。那个时候&#xff0c;人们还在使用机械计算器进行计算&#xff0c;而且这些计算器的功能都非常有限。 图灵提出这个概念的初衷&#xff0c;是为了解决所谓的“判定问题”&#xf…

机器人持续学习基准LIBERO系列4——robosuite最基本demo

0.前置 机器人持续学习基准LIBERO系列1——基本介绍与安装测试机器人持续学习基准LIBERO系列2——路径与基准基本信息机器人持续学习基准LIBERO系列3——相机画面可视化及单步移动更新 1.robosuite的相关资料 是基于MuJoCo的机器人学习方针环境&#xff0c;提供一套基准环境…

蓝桥杯省赛无忧 竞赛常用库函数 课件5 排序

01 sort简介 02 sort的用法 sort(起始地址&#xff0c;结束地址的下一位,比较函数);默认用小于号#include<bits/stdc.h> using namespace std; int main(){int a[1000];int n;//读取数组大小cin>>n;//读取元素for(int i1;i<n;i)cin>>a[i];//对数组进行排…

vue3+vite+ts+pinia新建项目(略详细版)

1、新建项目 npm create vite@latest 2、安装依赖 yarn add vue-router yarn add -D @types/node vite-plugin-pages sass sass-loader 3、配置别名 //vite.config.ts import { defineConfig } from vite import path from node:path export default defineConfig({ plu…

如何有效提高矢量网络分析仪的动态范围

动态范围是网络分析仪&#xff08;VNA&#xff09;接收机的最大输入功率与最小可测量功率&#xff08;本底噪声&#xff09;之间的差值&#xff0c;如图所示&#xff0c;要使测量有效&#xff0c;输入信号必须在这些边界内。 如果需要测量信号幅度非常大的变化&#xff0c;例如…

openai自定义API操作 API (openai.custom):OpenAI API 实现电商平台的智能库存管理

在电商行业中&#xff0c;库存管理是至关重要的环节之一。一个高效的库存管理系统可以确保商品的正常供应&#xff0c;避免缺货或积压现象&#xff0c;从而提高销售效率和客户满意度。然而&#xff0c;传统的库存管理方式往往存在一些问题&#xff0c;如数据不准确、响应不及时…

版本控制系统教程

1.Git的基本介绍 1.1 Git的概念 Git是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目.Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件.Git与常用的版本控制工具CVS&#xff0c;Subversion等不同&#xff…

JavaWEB学习笔记 2024-1-10 --JavaScript

上一篇 文章目录 3.JavaScript3.1第一个JS程序3.2JS的引入方式3.3JS中函数定义3.4函数的触发3.5查看浏览器控制台输出3.6数据类型3.7变量3.8运算符3.9流程控制语句3.10JS中的异常处理 3.JavaScript 是由网景公司发布,最开始的名字为LiveScript,之后和sun公司合作,改名为JavaSc…

[linux] git clone一个repo,包括它的子模块submodule

How do I "git clone" a repo, including its submodules? - Stack Overflow git clone git://github.com/foo/bar.git cd bar git submodule update --init --recursive

大模型关于Lora论文集合

《Chain of LoRA:Efficient Fine-tuning of Language Models via Residual Learning》 Chain of LoRA (COLA)&#xff0c;这是一种受 Frank-Wolfe 算法启发的迭代优化框架&#xff0c;旨在弥合 LoRA 和全参数微调之间的差距&#xff0c;而不会产生额外的计算成本或内存开销。CO…