实验1
任务1
源代码
// 现代C++标准库,算法库体验
// 本例用到以下内容:
// 1.字符串string,动态数组容器类vector,迭代器
// 2.算法库:反转元素次序,旋转元素
// 3.函数模板,const引用作为形参
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
// 模板函数声明
template<typename T>
void output(const T &c);
void test1();
void test2();
void test3();
int main() {
std::cout<<"测试1:\n";
test1();
std::cout<<"\n测试2:\n";
test2();
std::cout<<"\n测试3:\n";
test3();
}
// 输出容器对象c中的对象
template <typename T>
void output(const T &c) {
for(auto &i:c)
std::cout<<i<<' ';
std::cout<<'\n';
}
// 测试1:组合使用算法库,迭代器,string反转字符串
void test1() {
using namespace std;
string s0{"0123456789"};
cout<<"s0="<<s0<<endl;
string s1(s0);
// 反转s1自身
reverse(s1.begin(),s1.end());
cout<<"s1="<<s1<<endl;
string s2(s0.size(),' ');
// 将s0反转后结果拷贝到s2,s0自身不变
// 创建一个字符串 s2,长度和 s0 一样,每个字符都是空格 ' '
reverse_copy(s0.begin(),s0.end(),s2.begin());
// s0.begin(), s0.end():要反转的源区间,即整个 s0
// s2.begin():拷贝的目标起始位置
// 作用:把 s0 的字符从后往前依次写入 s2
cout<<"s2="<<s2<<endl;
}
// 测试2:组合使用算法库,迭代器,vector反转动态数组对象vector内数据
void test2() {
using namespace std;
vector<int> v0{2,0,4,9};
cout<<"v0:"; output(v0);
vector<int> v1{v0};
reverse(v1.begin(),v1.end());
cout<<"v1:"; output(v1);
vector<int> v2{v0};
reverse_copy(v0.begin(),v0.end(),v2.begin());
cout<<"v2:"; output(v2);
}
// 测试3:组合使用算法库,迭代器,vector实现元素旋转移位
void test3() {
using namespace std;
vector<int> v0{0,1,2,3,4,5,6,7,8,9};
cout<<"v0:"; output(v0);
vector<int> v1{v0};
// 将[v1.begin(),v1.end()]区间内元素循环左移1位
rotate(v1.begin(),v1.begin()+1,v1.end());
// rotate(first, middle, last) 有三个参数:
// first:区间起点
// middle:新的“第一个元素”所在位置(关键点)
// last:区间终点(开区间)
cout<<"v1:"; output(v1);
vector<int> v2{v0};
// 将[v1.begin(),v2.begin()]区间内元素循环左移2位
rotate(v2.begin(),v2.begin()+2,v2.end());
cout<<"v2:"; output(v2);
vector<int> v3{v0};
// 将[v1.begin(),v1.end()]去家内元素循环右移1位
rotate(v3.begin(),v3.end()-1,v3.end());
cout<<"v3:"; output(v3);
vector<int> v4{v0};
// 将[v1.begin(),v1.end()]区间内元素循环右移2位
rotate(v4.begin(),v4.end()-2,v4.end());
cout<<"v4:"; output(v4);
}
运行截图

问题
· 问题1: reverse()是将区间里面的元素直接反转,不产生新的内存分配,只需要传入两个参数,一个是反转区间起点,另一个是反转区间终点;reverse_copy()则是将区间里面的元素反转后复制在另一个目标区间,要传入三个参数:源区间起点,源区间终点,目标区间起点
· 问题2:rotate(first,middle,last):将区间[first,last)内的元素循环移动,让middle指向的元素成为新的第一个元素,first:区间起点,middle:新的第一个元素所在位置,last:区间终点(开区间)
任务2
源代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<numeric>
#include<iomanip>
#include<cstdlib>
#include<ctime>
// 模板函数声明
template<typename T>
void output(const T &c);
int generate_random_number();
void test1();
void test2();
int main() {
std::srand(std::time(0)); // 添加随机种子
// srand是seed random的缩写,time(0)相当于time(nullptr)只要返回值,不存储
std::cout<<"测试1:\n";
test1();
std::cout<<"\n测试2:\n";
test2();
}
// 输出容器对象c中的元素
template <typename T>
void output(const T &c) {
for(auto &i:c)
std::cout<<i<<' ';
std::cout<<'\n';
}
// 返回[0,100]区间内的一个随机整数
int generate_random_number() {
return std::rand()%101;
}
// 测试1:对容器类对象指定迭代器区间赋值,排序
void test1() {
using namespace std;
vector<int> v0(10); // 创建一个动态数组对象v0,对象大小为10
generate(v0.begin(),v0.end(),generate_random_number); // 生成随机数填充v0
// v0.begin(),v0.end() 指定要填充的区间,即整个容器 v0(从头到尾)
// generate_random_number 这是一个可调用对象(callable),必须满足:无参数、有返回值。每填充一个元素就调用它一次
cout<<"v0:"; output(v0);
vector<int> v1{v0};
sort(v1.begin(),v1.end()); // 对整个vector排序
cout<<"v1:"; output(v1);
vector<int> v2{v0};
sort(v2.begin()+1,v2.end()-1); // 只对中间部分排序,不包含首尾元素
cout<<"v2:"; output(v2);
}
// 测试2:对容器类对象指定迭代器区间赋值,计算最大值/最小值/均值
void test2() {
using namespace std;
vector<int> v0(10);
generate(v0.begin(),v0.end(),generate_random_number);
cout<<"v0:"; output(v0);
// 求最大值和最小值
auto min_iter=min_element(v0.begin(),v0.end());
auto max_iter=max_element(v0.begin(),v0.end());
cout<<"最小值:"<<*min_iter<<endl;
cout<<"最大值:"<<*max_iter<<endl;
// 同时求最大值和最小值
auto ans=minmax_element(v0.begin(),v0.end());
cout<<"最小值:"<<*(ans.first)<<endl;
cout<<"最大值:"<<*(ans.second)<<endl;
// 求平均值
double avg1=accumulate(v0.begin(),v0.end(),0.0)/v0.size();
cout<<"均值:"<<fixed<<setprecision(2)<<avg1<<endl;
sort(v0.begin(),v0.end());
double avg2=accumulate(v0.begin()+1,v0.end()-1,0.0)/(v0.size()-2);
cout<<"去掉最大值,最小值之后,均值:"<<avg2<<endl;
}
运行截图

问题
· 问题1:generate(fist,last,generator):first,last指定要填充的区间;generator是一个可调用对象,必须满足无参数,有返回值;generator会给区间内每一个元素调用一次generator,并把返回值赋给当前元素
· 问题2:只需要调用一次minmax_element就可以同时得到最大值和最小值,而min_element和max_element则需要分别调用才可以得到对应的最大值和最小值,用minmax_element只需要遍历一次容器,而min_element和max_element则需要遍历两次,所以minmax_element效率更高
· 问题3:效果相同,lamba写法:[捕获列表] (参数列表) -> 返回类型 { 函数体 }
以题目中的为例::
· []:捕获列表,空的,表示不需要从外界捕获任何变量;
· ():参数列表,空的,因为generate要求generator要满足无参数;
· ->:返回类型,被省略了,因为c++11起支持自动推导返回类型,因为std::rand()%101返回的是int,所以lamba自动推导出返回类型是int;
· {return std::rand()%101}:函数体,产生从0到100的随机数
任务3
源代码
#include<iostream>
#include<string>
#include<algorithm>
#include<cctype>
unsigned char func(unsigned char c);
void test1();
void test2();
int main() {
std::cout<<"测试1:字符串大小写转换\n";
test1();
std::cout<<"\n测试2:字符变换\n";
test2();
}
unsigned char func(unsigned char c) {
if(c=='z')
return 'a';
if(c=='Z')
return 'A';
if(std::isalpha(c))
return static_cast<unsigned char>(c+1);
return c;
}
// 字母都往后移1位,不是字母的原样返回
void test1() {
std::string s1{"Hello World 2049!"};
std::cout<<"s1="<<s1<<'\n';
std::string s2;
for(auto c:s1)
s2+=std::tolower(c);
std::cout<<"s2="<<s2<<'\n';
// 转小写
std::string s3;
for(auto c:s1)
s3+=std::toupper(c);
std::cout<<"s3="<<s3<<'\n';
// 转大写
}
void test2() {
std::string s1{"I love cosmos!"};
std::cout<<"s1="<<s1<<'\n';
std::string s2(s1.size(),' ');
std::transform(s1.begin(),s1.end(),
s2.begin(),
func);
std::cout<<"s2="<<s2<<'\n';
// 它是 <algorithm> 里的标准算法,作用一句话:遍历一个区间,对每个元素施加一个操作,把结果写到另一个区间。
// std::transform(first, last, d_first, op);
// 对 [first,last) 每个元素 *it,算 op(*it),写到 *d_first:
}
运行截图

问题
· 问题1:func()自定义此函数是为了让c里面的每一个字母都向后移一位,其中z变成a,Z变成A;如果不是字母的话就原样返回。
· 问题2:tolower(c):如果c是大写字母,那就转小写,如果c不是大写字母,就原样返回;toupper(c):如果c是小写字母,那就转大写,如果c不是小写字母,就原样返回。
· 问题3:transform()是
transform(first,last,d_first,op) 表示对[first,last)每个元素it,算op(it),写到*d_first。如果把把第三个参数从s2.begin()改成s1.begin(),就相当于对s1进行修改,那么后面的cout语句就应该改成std::cout<<"s1="<<s1<<'\n';
任务4
源代码
#include <iostream>
#include <string>
#include <algorithm>
bool is_palindrome(const std::string &s);
bool is_palindrome_ignore_case(const std::string &s);
int main() {
using namespace std;
string s;
// 多组输入,直到按下Ctrl+Z结束测试
while(cin >> s) {
cout << boolalpha
<< "区分大小写: " << is_palindrome(s) << "\n"
<< "不区分大小写: " << is_palindrome_ignore_case(s) << "\n\n";
}
}
// 函数is_palindrome定义
// 待补足
// ×××
bool is_palindrome(const std::string &s) {
std::string s1(s);
std::reverse(s1.begin(),s1.end());
if(s1==s)
return true;
return false;
}
// 函数is_palindrome_ignore_case定义
// 待补足
// ×××
bool is_palindrome_ignore_case(const std::string &s) {
std::string s1(s);
std::reverse(s1.begin(),s1.end());
std::string s2;
for(auto c:s)
s2+=static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
std::string s3;
for(auto c:s1)
s3+=static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
if(s2==s3)
return true;
return false;
}
运行截图

问题
·可以把while(cin>>s)改为wihle(getline(s))
这样运行截图就这样:

getline()会读取换行符之前的所有内容并丢弃换行符,所以输入的hello oop就可以原样保留。
任务5
源代码
#include <iostream>
#include <string>
#include <algorithm>
std::string dec2n(int x, int n = 2);
int main() {
int x;
while(std::cin >> x) {
std::cout << "十进制: " << x << '\n'
<< "二进制: " << dec2n(x) << '\n'
<< "八进制: " << dec2n(x, 8) << '\n'
<< "十二进制: " << dec2n(x, 12) << '\n'
<< "十六进制: " << dec2n(x, 16) << '\n'
<< "三十二进制: " << dec2n(x, 32) << "\n\n";
}
}
// 函数dec2n定义
// 待补足
// ×××
std::string dec2n(int x,int n) {
if(x==0) return "0";
const char digits[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string result="";
while(x>0)
{
int remainder=x%n;
result+=digits[remainder];
x/=n;
}
std::reverse(result.begin(),result.end());
return result;
}
运行截图

任务6
源代码
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<iomanip>
int main() {
// vector<string> s{"A","B","C","D","E","F","G",
// "H","I","J","K","L","M","N","O","P","Q","R","S","T","U",
// "V","W","X","Y","Z"};
std::string s{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
std::string s0;
for(auto c:s)
s0+=std::tolower(c);
std::cout<<" ";
for(auto c:s0)
std::cout<<" "<<c;
std::cout<<'\n';
for(int i=1;i<=26;i++) {
std::string s1{s};
rotate(s1.begin(),s1.begin()+i,s1.end());
std::cout<<std::right<<std::setw(2)<<i;
for(auto c:s1)
std::cout<<" "<<c;
std::cout<<'\n';
}
return 0;
}
运行截图

任务7
源代码
#include<iostream>
#include<iomanip>
#include<iomanip>
#include<cstdlib>
int main() {
std::srand(std::time(0));
int correct_count=0;
const int TOTAL_QUESTIONS=10;
for(int i=1;i<=10;i++)
{
int op=std::rand()%4;
int a,b;
int user_ans,correct_ans;
char operator_;
if(op==0) {
operator_='+';
a=std::rand()%10+1;
b=std::rand()%10+1;
correct_ans=a+b;
}
else if(op==1) {
operator_='-';
a=std::rand()%10+1;
b=std::rand()%10+1;
if(a<b)
std::swap(a,b);
correct_ans=a-b;
}
else if(op==2) {
operator_='*';
a=std::rand()%10+1;
b=std::rand()%10+1;
correct_ans=a*b;
}
else {
operator_='/';
b=std::rand()%10+1;
correct_ans=std::rand()%10+1;
a=correct_ans*b;
while(a>10) {
b=rand()%5+1;
correct_ans=rand()%(10/b)+1;
a=correct_ans*b;
}
}
std::cout<<a<<" "<<operator_<<" "<<b<<" "<<"="<<" ";
std::cin>>user_ans;
if(user_ans==correct_ans)
correct_count++;
}
double accuracy=(double)correct_count/TOTAL_QUESTIONS*100;
std::cout<<"正确率:"<<std::fixed<<std::setprecision(2)<<accuracy<<"%"<<std::endl;
}
运行截图
· 第一次

·第二次

实验总结
· 在最后一个实验里面本来想用min(),max()来解决减法中要保证前一个数字大于后一个数字的问题的,但是用了之后发现这个样没办法交换a和b的位置,导致打印出来还是a<b的减法问题,不知道有没有什么方法可以比较简便地运用它
浙公网安备 33010602011771号