From 7bfa5bdc8d144c05f5683d7d37afdbc690980f5d Mon Sep 17 00:00:00 2001 From: gameloader Date: Thu, 1 Dec 2022 20:23:49 +0800 Subject: [PATCH] completet homework --- content/posts/bj_homework.md | 1170 +++++++++++++++++++++++++++++++++- 1 file changed, 1159 insertions(+), 11 deletions(-) diff --git a/content/posts/bj_homework.md b/content/posts/bj_homework.md index 8c7d751..10fb732 100644 --- a/content/posts/bj_homework.md +++ b/content/posts/bj_homework.md @@ -18,7 +18,7 @@ author = "Logic" 1. **使用I/O流以文本方式建立一个文件test1.txt,写入字符"已成功写入文件!",用其他字处理程序(例如Windows记事本程序Notepad)打开,看看是否正确写入。** 本题比较简单,只需掌握C++中基本的I/O输入输出方式即可,这里重要的是掌握C++中流的概念。这一部分可以参考 _C++ Prime Plus(第6版)中文版_ 第17章相关内容。简单理解流相当于管道,在文件(硬件也可视为文件)和程序之间传递字节数据。因此文件流相关的类fstream对应的重载如<<和标准输入输出流iostream中的用法类似。则代码如下 - ```C++ + ```cpp #include #include using namespace std; @@ -43,7 +43,7 @@ author = "Logic" 这个题只是在第一个题的基础上结合面向对象编程,简单的声明一个类并构造两个成员函数,一个用于修改对象的信息,一个用于打印对象的信息。在打开文件流的时候用两种不同的方式打开文件流即可。 -```C++ +```cpp #include #include #include @@ -517,7 +517,8 @@ int main(int argc, char *argv[]) { > 设计一个用于人事管理的“人员”类。由于考虑到通用性,这里只抽象出所有类型人员都具有的属性:编号、性别、出生日期、身份证号等。其中“出生日期”声明为一个“日期”类内嵌对象。用成员函数实现对人员信息的录入和显示。要求包括:构造函数和析构函数、拷贝构造函数、内联成员函数、带默认形参值的成员函数、类的组合。 -```C++ +```cpp + #include #include #include @@ -580,7 +581,7 @@ inline void Person::display() { > 定义一个Cat类,拥有静态数据成员HowManyCats,记录Cat的对象个体数目;静态成员函数GetHowMany(),存取HowManyCats。非静态数据成员CatID记录当前对象的编号,成员函数GetCatID()存取CatID。设计程序测试这个类,生成若干个Cat对象,输出每个对象的数据成员值,体会静态成员和非静态成员的用法。 -```C++ +```cpp #include #include #include @@ -655,7 +656,7 @@ int main(int argc, char *argv[]) { 使用char指针指示字符串,在构造函数和set_string中为指针动态分配内存并将字符串拷贝过去,连接字符串则重新申请空间并释放之前申请的空间,然后进行字符串的拷贝。代码如下 -```C++ +```cpp // MyString.h #include @@ -684,7 +685,7 @@ public: }; ``` -```C++ +```cpp // MyString.cpp #include "MyString.h" @@ -756,7 +757,7 @@ int main(int argc, char *argv[]) { 这个学生类中的部分函数在之前作业中已经实现过,这里就直接拿来使用。主要是要将函数和数据进行封装。代码如下 -```C++ +```cpp #include #include #include @@ -899,7 +900,7 @@ int main() { 该题其实背后涉及的原理很复杂,在查阅资料后可以找到一种最简洁的实现方式,该方式也被称为Meyers' Singleton.其实现代码如下 -```C++ +```cpp #include #include #include @@ -995,7 +996,7 @@ int main(int argc, char *argv[]) { 这道题题面比较复杂。但是仔细分析后可知指挥部只有制造武士这一种行为,实际上比较简单。只需读取输入为每个武士赋予相应的属性即可。这里因为武士本身没有行为,可以使用结构体存储。司令部为一个类,其中包含了生命元数量,制造武士顺序,现在制造的武士的下标,司令部的颜色,武士的标号和所有武士的信息这几个成员变量。使用武士名作为key构造哈希表存储武士信息可以快速获得某种武士的数量。构造函数中初始化成员变量。成员函数为制作武士和判断两个函数。制造武士用于根据指定下标制造对应顺序的武士,判断则用于判断当前应制造的武士能否制造,不能则寻找下一个能制造的武士,如果已经不能制造则终止制造武士并将over置为true; 代码如下 -```C++ +```cpp #include #include #include @@ -1148,9 +1149,11 @@ int main(int argc, char *argv[]) { ### 完善字符串类 {#完善字符串类} > 编写封装完善的MyString类并测试,要求有多个构造函数、完成深拷贝的拷贝构造函数和赋值运算符函数以及一些其他功能函数;练习掌握内联函数的使用。 + 该题为完善之前的字符串类,需要为之前的字符串类添加深拷贝的拷贝构造函数,即在拷贝字符串的过程中为指针申请新的内存空间。同时还要重载赋值运算符‘=’,在重载运算符的过程中,若想直接修改赋值运算符的左值,则需使用this指针指向调用运算符的对象本身。重载的运算符可以看做对象的成员函数,即`object.operator=()`。返回对象本身即返回*this,对this指针解引用即可得到对象本身,代码如下 -```c++ +```cpp + // MyString.h #include #include @@ -1262,6 +1265,7 @@ int main(int argc, char *argv[]) { cout << str2.get_string() << endl; return 0; } + ``` 得到的运行结果为: @@ -1273,7 +1277,7 @@ int main(int argc, char *argv[]) { > 对于学生(包含姓名、学号、成绩等),请完善封装学生类CStudent(将之前使用的结构体STUDENT封装为该类),其中学生的姓名、专业要使用上题的MyString类的对象存储;完善封装学生列表类CStudentList保存数量不定的学生,并在这个类中实现增加学生、按学号删除、修改学生信息、显示学生信息的功能,显示学生信息的函数要有重载版本;为CStudentList类提供复制构造函数、赋值运算符函数;注意初始化列表的使用。 该题是将字符串类和之前构造的学生类进行组合。这道题的关键在于CStudentList类中的拷贝构造函数和赋值运算符的重载,这两部分的代码如下 -```c++ +```cpp // CStudentList的拷贝构造函数 CStudentList(const CStudentList &obj) { for (auto stu : obj.students) { @@ -1567,3 +1571,1147 @@ int main() { 运行的效果为 ![img](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1112H6w9uJ.png) + +## 第六次作业 {#第六次作业} +### 为MyString类提供重载的运算符 +> 完善之前的MyString类,为其编写赋值运算符函数实现直接使用字符串为其赋值的功能、编写加法(+)运算符函数实现两个对象的相加和一个对象加一个字符串的功能、编写下标运算符函数实现取指定索引的字符的功能、编写函数调用运算符实现使用字符串赋值的功能。编写上述函数后,下面的程序可完成所述功能。 +```cpp +MyString s1("hello "), s2("world!"); +const MyString s3 = s1 + s2; //对象s3内容为"hello world!",对象s1和s2不变 +s1 = s1 + "world!"; //对象s1的内容为"hello world!" +cout << s1[0] << s3[1] << endl;//输出为"he" +s1("I love C++, yeah!"); //对象s1内容为"I love C++, yeah!" +``` +该实验主要考验对更复杂的运算符的重载,按照各个运算符的重载规则重写对应的重载函数即可,这里一个比较有趣的问题在于,像`s3 = s1 + s2`这样的语句实际上的执行过程是先调用重载的加法运算符运算得到一个MyString对象,然后再根据MyString重载过的赋值运算符,将得到的MyString对象赋给s3。最后得到s3对象。在这个过程中实际上还使用了重载过的赋值运算符。全部代码如下 +```cpp +// MyString.h +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class MyString { +private: + char *str; + int length; + +public: + MyString(); + MyString(char *s); + MyString(const MyString &obj); + MyString &operator=(const MyString &obj); + MyString &operator=(const char *s); + MyString operator+(const MyString &obj); + MyString operator+(const char *s); + MyString &operator+(const char c); + char operator[](int i); + MyString &operator()(const char *s); + MyString &operator<=(const MyString &obj); + char *get_string(); + void set_string(char *s); + int get_length(); + void append(char *s); + void append(const MyString &obj); + ~MyString(); +}; +``` +```cpp +// MyString.cpp +#include "MyString.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +MyString::MyString() { cout << "MyString类的默认构造函数被调用" << endl; } + +MyString::MyString(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); + // cout << "MyString类的有参构造函数被调用,当前字符串为:" << str << endl; +} +MyString::~MyString() { + // cout << "MyString类的析构函数被调用,当前字符串为:" << str << endl; + delete str; +}; + +MyString::MyString(const MyString &obj) { + set_string(obj.str); + // cout << "MyString类的复制构造函数被调用,当前字符串为:"; + // cout << obj.str << endl; +} + +MyString &MyString::operator=(const MyString &obj) { + set_string(obj.str); + // cout << "MyString类的赋值运算符被调用,当前字符串为:"; + // cout << obj.str << endl; + return *this; +} + +// 使用字符串赋值 +MyString &MyString::operator=(const char *s) { + set_string((char *)s); + return *this; +} + +// 实现两个对象相加 +MyString MyString::operator+(const MyString &obj) { + MyString temp(str); + temp.append(obj.str); + return temp; +} + +// 实现对象和字符串相加 +MyString MyString::operator+(const char *s) { + MyString temp(str); + temp.append((char *)s); + return temp; +} + +// 实现下标运算符 +char MyString::operator[](int i) { + if (i >= length) { + cout << "超过最大索引" << endl; + return *str; + } else { + return *(str + i); + } +} + +// 重载函数调用运算符 +MyString &MyString::operator()(const char *s) { + set_string((char *)s); + return *this; +} + +char *MyString::get_string() { return str; } +void MyString::set_string(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); +} +int MyString::get_length() { return length; } + +void MyString::append(char *s) { + length = length + strlen(s); + char *str_temp = new char[length + 1]; + str_temp[length] = '\0'; + strncpy(str_temp, str, length + 1); + strncat(str_temp, s, length + 1); + delete str; + str = str_temp; +} + +void MyString::append(const MyString &obj) { this->append(obj.str); } + +int main(int argc, char *argv[]) { + MyString s1("hello"), s2("world!"); + MyString s3 = s1 + s2; + s1 = s1 + "world"; + cout << s1[0] << s3[1] << endl; + s1("I love C++,yeah!"); + cout << s1.get_string() << endl; + return 0; +} +``` +得到的运行结果为 +![运行结果](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1126ilorn4tkt6DZ.png) +### 为CStudentList类提供重载的运算符 +> 完善之前的CStudentList类,为其编写加法运算符函数实现两个对象的相加(即两个对象中的学生合并构成一个新的CStudentList类的对象)、编写下标运算符函数实现取指定索引的学生的功能。 +本实验需要完善之前编写的CStudentList类,重写加法运算符和下标运算符即可,这里注意的是map标准类的复制和访问都可以通过迭代器来完成。完善部分的代码如下 +```cpp + // students数据 + map students; + // CStudentList重载加法运算符 + CStudentList operator+(const CStudentList &obj) { + CStudentList newstudent; + newstudent.students = students; + newstudent.students.insert(obj.students.begin(), obj.students.end()); + return newstudent; + } + + // 重载下标运算符 + CStudent *operator[](int i) { + if (i >= students.size()) { + cout << "超过最大索引" << endl; + return students.begin()->second; + } + map::iterator iter = students.begin(); + for (int j = 0; j != i; j++) { + iter++; + } + return iter->second; + } + +``` +### 通过MyString类练习使用文件I/O +> 在之前的MyString类的基础上设计输入符和输出符的重载函数,并通过文本文件进行测试:要求将数量不定的MyString类的对象的内容保存在一个文件中,然后从该文件中读取数据恢复这些对象。同时练习使用文本文件和二进制文件完成以上功能。 +> +> 提示:输出MyString类对象的值时首先应输出其保存的字符串的长度,然后输出字符串。否则,在读取字符串恢复对象时,会因为不知道应该读取多少个字节而无法恢复对象。 +本题只需要重载输入输出符的函数即可,每次输入输出只需读取一行数据,可在main中使用循环完成对整个文件的读取,这里注意重载的输入输出符对应的输入输出流为系统流,和输入输出的具体文件无关,从根本上说,对控制台的输入输出也是文件输入输出的一种。所以只需对系统中的istream或者ostream进行读取或写入即可。完善部分的代码如下 +```cpp +// 重载输入输出运算符 +ostream &operator<<(ostream &os, MyString &obj) { + os << obj.length << " " << obj.str; + return os; +} +istream &operator>>(istream &os, MyString &obj) { + os >> obj.length; + char *data = new char[obj.length + 1]; + os >> data; + obj.set_string(data); + delete[] data; + return os; +} +``` +全部代码如下 +```cpp +#include "MyString.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +MyString::MyString() { cout << "MyString类的默认构造函数被调用" << endl; } + +MyString::MyString(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); + // cout << "MyString类的有参构造函数被调用,当前字符串为:" << str << endl; +} +MyString::~MyString() { + // cout << "MyString类的析构函数被调用,当前字符串为:" << str << endl; + delete str; +}; + +MyString::MyString(const MyString &obj) { + set_string(obj.str); + // cout << "MyString类的复制构造函数被调用,当前字符串为:"; + // cout << obj.str << endl; +} + +MyString &MyString::operator=(const MyString &obj) { + set_string(obj.str); + // cout << "MyString类的赋值运算符被调用,当前字符串为:"; + // cout << obj.str << endl; + return *this; +} + +// 使用字符串赋值 +MyString &MyString::operator=(const char *s) { + set_string((char *)s); + return *this; +} + +// 实现两个对象相加 +MyString MyString::operator+(const MyString &obj) { + MyString temp(str); + temp.append(obj.str); + return temp; +} + +// 实现对象和字符串相加 +MyString MyString::operator+(const char *s) { + MyString temp(str); + temp.append((char *)s); + return temp; +} + +// 实现下标运算符 +char MyString::operator[](int i) { + if (i >= length) { + cout << "超过最大索引" << endl; + return *str; + } else { + return *(str + i); + } +} + +// 重载函数调用运算符 +MyString &MyString::operator()(const char *s) { + set_string((char *)s); + return *this; +} + +// 重载输入输出运算符 +ostream &operator<<(ostream &os, MyString &obj) { + os << obj.length << " " << obj.str; + return os; +} +istream &operator>>(istream &os, MyString &obj) { + os >> obj.length; + char *data = new char[obj.length + 1]; + os >> data; + obj.set_string(data); + delete[] data; + return os; +} + +char *MyString::get_string() { return str; } +void MyString::set_string(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); +} +int MyString::get_length() { return length; } + +void MyString::append(char *s) { + length = length + strlen(s); + char *str_temp = new char[length + 1]; + str_temp[length] = '\0'; + strncpy(str_temp, str, length + 1); + strncat(str_temp, s, length + 1); + delete str; + str = str_temp; +} + +void MyString::append(const MyString &obj) { this->append(obj.str); } + +int main(int argc, char *argv[]) { + MyString s2; + ifstream infile; + infile.open("test.dat"); + while (!infile.eof()) { + infile >> s2; + cout << s2.get_string() << endl; + } + return 0; +} +``` +```cpp +// MyString.h +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class MyString { +private: + char *str; + int length; + +public: + MyString(); + MyString(char *s); + MyString(const MyString &obj); + MyString &operator=(const MyString &obj); + MyString &operator=(const char *s); + MyString operator+(const MyString &obj); + MyString operator+(const char *s); + MyString &operator+(const char c); + char operator[](int i); + MyString &operator()(const char *s); + MyString &operator<=(const MyString &obj); + // 输出运算符函数 + friend ostream &operator<<(ostream &os, MyString &obj); + // 输入运算符函数 + friend istream &operator>>(istream &os, MyString &obj); + char *get_string(); + void set_string(char *s); + int get_length(); + void append(char *s); + void append(const MyString &obj); + ~MyString(); +}; + +``` +`test.dat`为提前准备好的文件,其内容为 +![内容](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1127Zry88Bt5hhKJ.png) +运行结果如下 +![结果](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1127XA37f6o04e7x.png) + +### 通过学生列表类练习使用文件I/O +> 在之前的CStudentList类和CStudent类的基础上,为它们编写文本文件和二进制文件的输入输出功能并测试。请注意,在输入输出CStudent类的MyString类的数据成员时需要通过MyString类的输入输出函数实现;在输入输出CStudentList中的CStudent类的对象时需要通过CStudent类的输入输出函数实现。 +这里封装的思想和MyString类类似,在文件头指明学生的数量。要说明的是在对输入输出符进行重载时需要声明其为对应类的友元函数,因为重载的是iostream类下的输入输出运算符,要使其能访问其他类中的成员则需将其声明为该类的友元函数。在使用时用该类的引用作为参数即可直接向文件中输入输出对应的对象的内容。 +测试结果如下,测试输出到test.dat文件中如下 +![](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1201nxXULMnlyCvT.png) + +文件中的数据为 +![](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1201NWARUWN4NgyM.png) + +从文件中读取如下 +![](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1201FvUrBFA22cuk.png) + +关键代码为 +```cpp + // 重载输出输入函数 + friend ostream &operator<<(ostream &os, CStudentList &stulist) { + os << stulist.students.size() << " "; + for (auto stu : stulist.students) { + os << *stu.second; + } + return os; + } + + friend istream &operator>>(istream &os, CStudentList &stulist) { + int size; + os >> size; + for (int i = 0; i < size; i++) { + CStudent *newstudent = new CStudent; + os >> *newstudent; + stulist.students.insert(make_pair(newstudent->sno, newstudent)); + } + return os; + } + +``` + +全部代码如下 +```cpp +#include "MyString.cpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +// CStudent类 +class CStudent { +public: + int sno; + MyString name; + MyString specialty; + int score; + CStudent(){}; + ~CStudent(){}; + + // Cstudent类输入输出重载 + friend ostream &operator<<(ostream &os, CStudent &stu) { + os << stu.name << " " << stu.score << " " << stu.sno << " " << stu.specialty + << endl; + return os; + } + + friend istream &operator>>(istream &os, CStudent &stu) { + os >> stu.name >> stu.score >> stu.sno >> stu.specialty; + return os; + } + +private: +}; + +class CStudentList { +public: + CStudentList(){}; + // CStudentList的拷贝构造函数 + CStudentList(const CStudentList &obj) { + for (auto stu : obj.students) { + CStudent *stu_temp = new CStudent(); + stu_temp->sno = stu.second->sno; + // 使用了MyString的拷贝构造函数 + stu_temp->name = stu.second->name; + stu_temp->score = stu.second->score; + stu_temp->specialty = stu.second->specialty; + students.insert(make_pair(stu.second->sno, stu_temp)); + } + } + // 赋值运算符函数 + CStudentList &operator=(const CStudentList &obj) { + for (auto stu : obj.students) { + CStudent *stu_temp = new CStudent(); + stu_temp->sno = stu.second->sno; + // 使用了MyString的拷贝构造函数 + stu_temp->name = stu.second->name; + stu_temp->score = stu.second->score; + stu_temp->specialty = stu.second->specialty; + students.insert(make_pair(stu.second->sno, stu_temp)); + this->students.insert(make_pair(stu.second->sno, stu_temp)); + } + return *this; + } + ~CStudentList() { + for (auto stu : students) { + delete &stu.second->name; + delete &stu.second->specialty; + } + }; + // students数据 + map students; + + // CStudentList重载加法运算符 + CStudentList operator+(const CStudentList &obj) { + CStudentList newstudent; + newstudent.students = students; + newstudent.students.insert(obj.students.begin(), obj.students.end()); + return newstudent; + } + + // 重载下标运算符 + CStudent *operator[](int i) { + if (i >= students.size()) { + cout << "超过最大索引" << endl; + return students.begin()->second; + } + map::iterator iter = students.begin(); + for (int j = 0; j != i; j++) { + iter++; + } + return iter->second; + } + + // 重载输出输入函数 + friend ostream &operator<<(ostream &os, CStudentList &stulist) { + os << stulist.students.size() << " "; + for (auto stu : stulist.students) { + os << *stu.second; + } + return os; + } + + friend istream &operator>>(istream &os, CStudentList &stulist) { + int size; + os >> size; + for (int i = 0; i < size; i++) { + CStudent *newstudent = new CStudent; + os >> *newstudent; + stulist.students.insert(make_pair(newstudent->sno, newstudent)); + } + return os; + } + + // 有默认形参,使用引用变量 + void add_stu(int sno, char *name, char *specialty, int score = 60) { + CStudent *student = new CStudent(); + student->sno = sno; + student->score = score; + student->name.set_string(name); + student->specialty.set_string(specialty); + students.insert(make_pair(sno, student)); + } + + void del_stu(int sno) { + // 释放内存 + delete (students.find(sno)->second); + students.erase(sno); + cout << "Delete success!"; + } + // 默认全部显示 + void look_up_stu() { + for (auto stu : students) { + cout << "Sno = " << stu.second->sno + << " Name = " << stu.second->name.get_string() + << " Specialty = " << stu.second->specialty.get_string() + << " Score = " << stu.second->score << endl; + } + } + + // 重载的显示函数 + void look_up_stu(int flag, int min = 0, int max = 100) { + for (auto stu : students) { + if ((stu.second->score) >= min && stu.second->score <= max) { + cout << "Sno = " << stu.second->sno + << " Name = " << stu.second->name.get_string() + << " Specialty = " << stu.second->specialty.get_string() + << " Score = " << stu.second->score << endl; + } + } + } + + // 根据学号修改学生信息 + void change_stu(int sno, char *name, char *specialty, int score = 60) { + int keycount = students.count(sno); + if (keycount != 1) { + cout << "Don't have this student, please add it to system!!" << endl; + return; + } else { + students.find(sno)->second->sno = sno; + students.find(sno)->second->name.set_string(name); + students.find(sno)->second->specialty.set_string(specialty); + students.find(sno)->second->score = score; + cout << "Update success!" << endl; + return; + } + } +}; + +int main() { + char select; + cout << "Welcome to my student manage system!!!!!" << endl; + CStudentList *stu_info = new CStudentList(); + while (true) { + cout << "\nPlease select the operation you want:" << endl + << "\t1.add a student.\n" + << "\t2.delete a student\n" + << "\t3.display students information\n" + << "\t4.change student information\n" + << "\t5.write to a file\n" + << "\t6.read from a file\n" + << "\tq for quit\n" + << "your select:"; + cin >> select; + switch (select) { + case '1': { + int number, average; + string aver; + string name, specialty; + cout << "The student number:"; + cin >> number; + cout << "The student name:"; + cin >> name; + cout << "The student specialty:"; + cin >> specialty; + cin.get(); + cout << "The student score(can be nothing):"; + getline(cin, aver); + if (aver.length() == 0) { + stu_info->add_stu(number, (char *)name.data(), + (char *)specialty.data()); + } else { + average = stoi(aver); + stu_info->add_stu(number, (char *)name.data(), (char *)specialty.data(), + average); + } + break; + } + case '2': { + int number; + cout << "The student number:"; + cin >> number; + stu_info->del_stu(number); + break; + } + case '3': { + string min, max; + cout << "Enter the min score of the :"; + cin.get(); + getline(cin, min); + cout << "Enter the max score of the :"; + getline(cin, max); + if (min.length() == 0 && max.length() == 0) { + stu_info->look_up_stu(); + break; + } else if (max.length() == 0) { + stu_info->look_up_stu(1, stoi(min)); + } else if (min.length() == 0) { + stu_info->look_up_stu(1, 0, stoi(max)); + } else { + stu_info->look_up_stu(1, stoi(min), stoi(max)); + } + break; + } + case '4': { + int number, average; + string aver; + string name, specialty; + cout << "The student number:"; + cin >> number; + cout << "The student name:"; + cin >> name; + cout << "The student specialty:"; + cin >> specialty; + cin.get(); + cout << "The student score(can be nothing):"; + getline(cin, aver); + if (aver.length() == 0) { + stu_info->change_stu(number, (char *)name.data(), + (char *)specialty.data()); + } else { + average = stoi(aver); + stu_info->change_stu(number, (char *)name.data(), + (char *)specialty.data(), average); + } + break; + } + case '5': { + cout << "please enter the file name:"; + string filename; + cin >> filename; + ofstream outfile; + outfile.open(filename); + outfile << *stu_info; + outfile.close(); + break; + } + case '6': { + ifstream infile; + cout << "please enter the file name:"; + string filename; + cin >> filename; + infile.open(filename); + infile >> *stu_info; + infile.close(); + break; + } + case 'q': { + return 0; + } + default: + break; + } + } +} + +``` + + +### 大整数运算 +> 封装一个大整数类HugeInt,以支持任意大整数的四则运算,重载你认为需要的所有运算符,比如=,==,>等等运算符。 +> +> 测试类是否正确:如计算 n=100! + 50! + +通过字符串运算及位运算可重载各种运算符,代码如下 +```cpp +#include +#include +#include +#include +using namespace std; +/* + ###################################################################### + ####################### THE BIG INT ########################## +*/ +const int base = 1000000000; +const int base_digits = 9; +struct bigint { + vector a; + int sign; + /**/ + int size() { + if (a.empty()) + return 0; + int ans = (a.size() - 1) * base_digits; + int ca = a.back(); + while (ca) + ans++, ca /= 10; + return ans; + } + bigint operator^(const bigint &v) { + bigint ans = 1, a = *this, b = v; + while (!b.isZero()) { + if (b % 2) + ans *= a; + a *= a, b /= 2; + } + return ans; + } + string to_string() { + stringstream ss; + ss << *this; + string s; + ss >> s; + return s; + } + int sumof() { + string s = to_string(); + int ans = 0; + for (auto c : s) + ans += c - '0'; + return ans; + } + /**/ + bigint() : sign(1) {} + + bigint(long long v) { *this = v; } + + bigint(const string &s) { read(s); } + + void operator=(const bigint &v) { + sign = v.sign; + a = v.a; + } + + void operator=(long long v) { + sign = 1; + a.clear(); + if (v < 0) + sign = -1, v = -v; + for (; v > 0; v = v / base) + a.push_back(v % base); + } + + bigint operator+(const bigint &v) const { + if (sign == v.sign) { + bigint res = v; + + for (int i = 0, carry = 0; i < (int)max(a.size(), v.a.size()) || carry; + ++i) { + if (i == (int)res.a.size()) + res.a.push_back(0); + res.a[i] += carry + (i < (int)a.size() ? a[i] : 0); + carry = res.a[i] >= base; + if (carry) + res.a[i] -= base; + } + return res; + } + return *this - (-v); + } + + bigint operator-(const bigint &v) const { + if (sign == v.sign) { + if (abs() >= v.abs()) { + bigint res = *this; + for (int i = 0, carry = 0; i < (int)v.a.size() || carry; ++i) { + res.a[i] -= carry + (i < (int)v.a.size() ? v.a[i] : 0); + carry = res.a[i] < 0; + if (carry) + res.a[i] += base; + } + res.trim(); + return res; + } + return -(v - *this); + } + return *this + (-v); + } + + void operator*=(int v) { + if (v < 0) + sign = -sign, v = -v; + for (int i = 0, carry = 0; i < (int)a.size() || carry; ++i) { + if (i == (int)a.size()) + a.push_back(0); + long long cur = a[i] * (long long)v + carry; + carry = (int)(cur / base); + a[i] = (int)(cur % base); + // asm("divl %%ecx" : "=a"(carry), "=d"(a[i]) : "A"(cur), "c"(base)); + } + trim(); + } + + bigint operator*(int v) const { + bigint res = *this; + res *= v; + return res; + } + + void operator*=(long long v) { + if (v < 0) + sign = -sign, v = -v; + for (int i = 0, carry = 0; i < (int)a.size() || carry; ++i) { + if (i == (int)a.size()) + a.push_back(0); + long long cur = a[i] * (long long)v + carry; + carry = (int)(cur / base); + a[i] = (int)(cur % base); + // asm("divl %%ecx" : "=a"(carry), "=d"(a[i]) : "A"(cur), "c"(base)); + } + trim(); + } + + bigint operator*(long long v) const { + bigint res = *this; + res *= v; + return res; + } + + friend pair divmod(const bigint &a1, const bigint &b1) { + int norm = base / (b1.a.back() + 1); + bigint a = a1.abs() * norm; + bigint b = b1.abs() * norm; + bigint q, r; + q.a.resize(a.a.size()); + + for (int i = a.a.size() - 1; i >= 0; i--) { + r *= base; + r += a.a[i]; + int s1 = r.a.size() <= b.a.size() ? 0 : r.a[b.a.size()]; + int s2 = r.a.size() <= b.a.size() - 1 ? 0 : r.a[b.a.size() - 1]; + int d = ((long long)base * s1 + s2) / b.a.back(); + r -= b * d; + while (r < 0) + r += b, --d; + q.a[i] = d; + } + + q.sign = a1.sign * b1.sign; + r.sign = a1.sign; + q.trim(); + r.trim(); + return make_pair(q, r / norm); + } + + bigint operator/(const bigint &v) const { return divmod(*this, v).first; } + + bigint operator%(const bigint &v) const { return divmod(*this, v).second; } + + void operator/=(int v) { + if (v < 0) + sign = -sign, v = -v; + for (int i = (int)a.size() - 1, rem = 0; i >= 0; --i) { + long long cur = a[i] + rem * (long long)base; + a[i] = (int)(cur / v); + rem = (int)(cur % v); + } + trim(); + } + + bigint operator/(int v) const { + bigint res = *this; + res /= v; + return res; + } + + int operator%(int v) const { + if (v < 0) + v = -v; + int m = 0; + for (int i = a.size() - 1; i >= 0; --i) + m = (a[i] + m * (long long)base) % v; + return m * sign; + } + + void operator+=(const bigint &v) { *this = *this + v; } + void operator-=(const bigint &v) { *this = *this - v; } + void operator*=(const bigint &v) { *this = *this * v; } + void operator/=(const bigint &v) { *this = *this / v; } + + bool operator<(const bigint &v) const { + if (sign != v.sign) + return sign < v.sign; + if (a.size() != v.a.size()) + return a.size() * sign < v.a.size() * v.sign; + for (int i = a.size() - 1; i >= 0; i--) + if (a[i] != v.a[i]) + return a[i] * sign < v.a[i] * sign; + return false; + } + + bool operator>(const bigint &v) const { return v < *this; } + bool operator<=(const bigint &v) const { return !(v < *this); } + bool operator>=(const bigint &v) const { return !(*this < v); } + bool operator==(const bigint &v) const { + return !(*this < v) && !(v < *this); + } + bool operator!=(const bigint &v) const { return *this < v || v < *this; } + + void trim() { + while (!a.empty() && !a.back()) + a.pop_back(); + if (a.empty()) + sign = 1; + } + + bool isZero() const { return a.empty() || (a.size() == 1 && !a[0]); } + + bigint operator-() const { + bigint res = *this; + res.sign = -sign; + return res; + } + + bigint abs() const { + bigint res = *this; + res.sign *= res.sign; + return res; + } + + long long longValue() const { + long long res = 0; + for (int i = a.size() - 1; i >= 0; i--) + res = res * base + a[i]; + return res * sign; + } + + friend bigint gcd(const bigint &a, const bigint &b) { + return b.isZero() ? a : gcd(b, a % b); + } + friend bigint lcm(const bigint &a, const bigint &b) { + return a / gcd(a, b) * b; + } + + void read(const string &s) { + sign = 1; + a.clear(); + int pos = 0; + while (pos < (int)s.size() && (s[pos] == '-' || s[pos] == '+')) { + if (s[pos] == '-') + sign = -sign; + ++pos; + } + for (int i = s.size() - 1; i >= pos; i -= base_digits) { + int x = 0; + for (int j = max(pos, i - base_digits + 1); j <= i; j++) + x = x * 10 + s[j] - '0'; + a.push_back(x); + } + trim(); + } + + friend istream &operator>>(istream &stream, bigint &v) { + string s; + stream >> s; + v.read(s); + return stream; + } + + friend ostream &operator<<(ostream &stream, const bigint &v) { + if (v.sign == -1) + stream << '-'; + stream << (v.a.empty() ? 0 : v.a.back()); + for (int i = (int)v.a.size() - 2; i >= 0; --i) + stream << setw(base_digits) << setfill('0') << v.a[i]; + return stream; + } + + static vector convert_base(const vector &a, int old_digits, + int new_digits) { + vector p(max(old_digits, new_digits) + 1); + p[0] = 1; + for (int i = 1; i < (int)p.size(); i++) + p[i] = p[i - 1] * 10; + vector res; + long long cur = 0; + int cur_digits = 0; + for (int i = 0; i < (int)a.size(); i++) { + cur += a[i] * p[cur_digits]; + cur_digits += old_digits; + while (cur_digits >= new_digits) { + res.push_back(int(cur % p[new_digits])); + cur /= p[new_digits]; + cur_digits -= new_digits; + } + } + res.push_back((int)cur); + while (!res.empty() && !res.back()) + res.pop_back(); + return res; + } + + typedef vector vll; + + static vll karatsubaMultiply(const vll &a, const vll &b) { + int n = a.size(); + vll res(n + n); + if (n <= 32) { + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + res[i + j] += a[i] * b[j]; + return res; + } + + int k = n >> 1; + vll a1(a.begin(), a.begin() + k); + vll a2(a.begin() + k, a.end()); + vll b1(b.begin(), b.begin() + k); + vll b2(b.begin() + k, b.end()); + + vll a1b1 = karatsubaMultiply(a1, b1); + vll a2b2 = karatsubaMultiply(a2, b2); + + for (int i = 0; i < k; i++) + a2[i] += a1[i]; + for (int i = 0; i < k; i++) + b2[i] += b1[i]; + + vll r = karatsubaMultiply(a2, b2); + for (int i = 0; i < (int)a1b1.size(); i++) + r[i] -= a1b1[i]; + for (int i = 0; i < (int)a2b2.size(); i++) + r[i] -= a2b2[i]; + + for (int i = 0; i < (int)r.size(); i++) + res[i + k] += r[i]; + for (int i = 0; i < (int)a1b1.size(); i++) + res[i] += a1b1[i]; + for (int i = 0; i < (int)a2b2.size(); i++) + res[i + n] += a2b2[i]; + return res; + } + + bigint operator*(const bigint &v) const { + vector a6 = convert_base(this->a, base_digits, 6); + vector b6 = convert_base(v.a, base_digits, 6); + vll a(a6.begin(), a6.end()); + vll b(b6.begin(), b6.end()); + while (a.size() < b.size()) + a.push_back(0); + while (b.size() < a.size()) + b.push_back(0); + while (a.size() & (a.size() - 1)) + a.push_back(0), b.push_back(0); + vll c = karatsubaMultiply(a, b); + bigint res; + res.sign = sign * v.sign; + for (int i = 0, carry = 0; i < (int)c.size(); i++) { + long long cur = c[i] + carry; + res.a.push_back((int)(cur % 1000000)); + carry = (int)(cur / 1000000); + } + res.a = convert_base(res.a, 6, base_digits); + res.trim(); + return res; + } +}; +/* + ####################### THE BIG INT ########################## + ###################################################################### +*/ + +string a, b; +int main(void) { + bigint first = 1, second = 1; + for (int i = 1; i < 101; i++) { + first *= i; + } + for (int i = 1; i < 51; i++) { + second *= i; + } + bigint result = first + second; + cout << result.to_string(); + return 0; +} + +``` + +计算100!+50!的结果为 +![](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/1201Yr6K7cV6b3Kf.png) + + + + +## Python作业 {#Python作业} +### pyecharts在jupyter notebook的渲染问题 +pyecharts的图形在jupyter notebook中可能会不显示,比较好的解决方案是在使用时在顶部声明环境类型,在python代码的顶部添加如下代码并重启jupyter notebook即可,环境类型在pyecharts的文档中可以找到。 +```python +from pyecharts.globals import CurrentConfig, NotebookType +CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_NOTEBOOK +``` +重启jupyter服务即可发现pyecharts的图表可以正常加载了。 +### 实验需求分析 +本次实验给出了动车故障相关的样例数据信息,通过这些信息可以对数据进行多方面的分析,通过pandas处理数据,再将处理后的数据传给pyecharts,通过pyecharts对数据进行可视化的展示,再利用flask作为承载可视化展示的web服务框架,从而通过网页可直接访问处理后的可视化数据。对数据的分析可以有多个角度的选择,如可以展示各个月份发生的故障数目的折线图,各个月份发生故障类型的条形图,某种故障对应的责任公司占比饼图等等。 +### 程序设计与实现 + + + + +