实验一
任务1
- 程序源代码
task1.cpp
#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 << "测试2:\n";
test2();
std::cout << "测试3:\n";
test3();
}
template<typename T>
void output(const T& c) {
for (auto& i : c)
std::cout << i << ' ';
std::cout << '\n';
}
void test1() {
using namespace std;
string s0{ "0123456789" };
cout<< "s0=" << s0 << endl;
string s1(s0);
reverse(s1.begin(), s1.end());
cout << "s1=" << s1 << endl;
string s2(s0.size(), ' ');
reverse_copy(s0.begin(), s0.end(), s2.begin());//将s0反转后的结果拷贝到s2,s0自身不变
cout << "s2=" << s2 << endl;
}
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);
}
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 };
rotate(v1.begin(), v1.begin() + 1, v1.end());
cout << "v1:"; output(v1);
vector<int>v2{ v0 };
rotate(v2.begin(), v2.begin() + 2, v2.end());
cout << "v2:"; output(v2);
vector<int>v3{ v0 };
rotate(v3.begin(), v3.end() - 1, v3.end());
cout << "v3:"; output(v3);
vector<int>v4{ v0 };
rotate(v4.begin(), v4.end() - 2, v4.end());
cout << "v4:"; output(v4);
}
- 运行截图

- 观察与思考
1.reverse和reverse_copy有什么区别?
reverse:是原地修改。
reverse_copy:是把反转的结果拷贝到新的变量里,原变量不变。
2.rotate算法是如何改变元素顺序的?它的三个参数分别代表什么?
rotate函数是包含在
std::rotate(first,middle,last)
旋转前元素顺序:[first,middle)[middle,last)
旋转后元素顺序:[middle,last)[first,middle)
它相当于把元素划分为first、middle、last三部分。
任务2
- 程序源代码
task2.cpp
#include<iostream>
#include<vector>
#include<algorithm>
#include<numeric>
#include<cstdlib>
#include<ctime>
#include<iomanip>
template<typename t>
void output(const t& c);//模板函数声明
int generate_random_number();
void test1();
void test2();
int main() {
std::srand(std::time(0));//添加随机种子
std::cout << "测试1:\n";
test1();
std::cout << "测试2:\n";
test2();
}
template<typename t>
void output(const t& c) {
for (auto& i : c)
std::cout << i << ' ';
std::cout << "\n";
}
int generate_random_number() {
return std::rand() % 101;//返回[0,100]区间内的一个随机整数
}
void test1() {
using namespace std;
vector<int>v0(10);//创建一个动态数组对象v0,对象大小为10
generate(v0.begin(), v0.end(), generate_random_number);//生成随机数填充v0
cout << "v0:"; output(v0);
vector<int>v1{ v0 };
sort(v1.begin(), v1.end());
cout << "v1:"; output(v1);
vector<int>v2{ v0 };
sort(v2.begin() + 1, v2.end() - 1);
cout << "v2:"; output(v2);
}
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;//老师给的代码少了#include<iomanip>
sort(v0.begin(), v0.end());
double avg2 = accumulate(v0.begin() + 1, v0.end() - 1, 0.0) / (v0.size () - 2);
cout << "去掉最大值、最小值之后,均值:" << avg2 << endl;
}
- 运行截图

- 观察与思考
1.generate算法的作用是什么?
generate(v.begin(),v.end(),num)
用num填充数组,第一个参数是起始位置,第二个参数是终止位置,第三个参数是填充物。
2.minmax_element和分别调用min_element、max_elemnet相比,有什么优势?
minmax_element只扫描一遍容器,时间开销更小。分别调用min_element、max_element需要扫描两遍容器。
减少内存访问,缓存友好。分开调用要读两遍,容易造成更多cache miss
代码更简洁,一次性拿到两个迭代器。
3.把代码中函数generate_random_number的声明和定义注释起来,把两处调用改成如下写法,观察效果是否等同?查阅c++中lambda表达式用法。
//test1()模块和test2()模块:
generate(v0.begin(),v0.end(),generate_random_number);
---->改成
generate(v0.begin(),v0.end(),[](){return std::rand()%101;});
效果等同。
完整语法格式
[捕获列表](参数列表) mutable -> 返回值类型 { 函数体 }
- []:捕获列表,用来捕获外面作用域的变量
- ():形参列表,和普通函数参数一样
- mutable(可选):允许修改值捕获的变量
- ->类型(可选):尾置返回类型;能自动推导时可以省略
- {}:函数体
一、捕获列表(最常用4种)
- [] 不捕获任何外部变量(generate代码就是这个)
[](){ return rand()%101; }
- [=] 值捕获:把外部变量拷贝一份进来,默认不能修改
int a=10;
auto f = [=](){ return a+1; };
- [&] 引用捕获:直接引用外部原变量,可以修改原值
int a=10;
auto f = [&](){ a++; };
- [x, &y] 混合捕获:x值捕获,y引用捕获
捕获只能捕获当前作用域的变量,不能跨函数随便抓。
二、generate代码例子拆解
generate(v0.begin(), v0.end(), []() {return std::rand() % 101; });
- []:不捕获外部变量
- ():无参数
- 省略返回值:编译器自动推导int
- {return std::rand()%101;}:函数体,返回0~100随机数>
generate会反复调用这个lambda,给vector每个元素赋值
任务三
- 程序源代码
task3.cpp
#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;//只要执行了return函数立马结束
}
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 <<std::endl;
std::string s3;
for (auto c : s1)
s3 += std::toupper(c);
std::cout << "s3=" << s3 << "\n";
//什么是迭代器
//for (auto it1 = s1.begin(), it2 = s2.begin();
//it1 != s1.end();
//++it1, ++it2) {
//*it2 = func(*it1);
//}这个代码里面最后一行为什么有* 是表示引用类型吗
//这表示取迭代器里的参数,取出it1指向的参数
}
void test2() {
std::string s1{ "I love cosmos!" };
std::cout << "s1=" << s1 << '\n';
std::string s2(s1.size(), ' ');//先为s2分配足够大内存,它不会自动扩容
std::transform(s1.begin(), s1.end(), s2.begin(), func);//对s1的每个字符调用func,并将返回值填入s2中
//若第三个参数是s1.begin(),他也可以修改原字符串
std::cout << "s2=" << s2 << '\n';
//std::string s3;
//std::transform(s1.begin(), s1.end(), std::back_inserter(s3), func);//自动追加,更安全
//std::cout << "s3=" << s3 << '\n';
}
- 运行截图

- 观察与思考
1.自定义函数func功能是什么?
将字符串加密,加密方法就是将所有字母按照字母表顺序往后顺延一位,并且转换为a
2.tolower和toupper功能分别是什么?
tolowr:将所有字母转换成小写
toupper:将所有字母转换成大写
3.transform的4个参数意义分别是什么?如果把第三个参数s2.begin()改成s1.begin(),有何区别?
第一个参数是读取的起始位置,第二个参数是读取的终止位置,第三个参数是新元素的存放起始位置,第四个参数是修改元素的规则。
task.3cpp代码中表示对s1中每一个字符都调用函数func,并将修改后元素保存在s2中
若改成s1.begin(),那么就变成原地修改,修改后的元素在s1中。
任务四
- 程序源代码
task4.cpp
#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;
while (cin >> s) {
cout << boolalpha << "区分大小写:" << is_palindrome(s) << "\n"
<< "不区分大小写:" << is_palindrome_ignore_case(s) << "\n\n";
//多组输入,直到按下crl+z结束测试
}
}
bool is_palindrome(const std::string& s) {
int left = 0;
int right = s.size() - 1;//获取字符串长度
while (left < right) {
//if (left == right == 1)
//return true;
if (s[left] != s[right]) {//字符串可以直接通过下标访问
return false;
}
left++;
right--;
}
return true;
}
bool is_palindrome_ignore_case(const std::string& s) {
std::string s2( s.size(),' ' );
std::transform(s.begin(), s.end(), s2.begin(), std::tolower);
return is_palindrome(s2);
}
- 运行截图

- 观察与思考
使用cin>>s输入时,输入在字符串中不能包含空格。如果希望测试字符串包含空格(如hello opp),代码应如何调整?
cin>>s遇到空格、回车就停止读取,没法读入带空格字符串。换成getline(cin,s)读取一整行(包含空格)
注意:如果前面混用cin,缓冲区会残留换行符,要cin.ignore()清空。
int main(){
using namespace std;
string str;
while(getline(cin,str)){
cout << boolalpha << "区分大小写:" << is_palindrome(str) << "\n"
<< "不区分大小写:" << is_palindrome_ignore_case(str) << "\n\n";
//多组输入,直到按下crl+z结束测试
}
}
任务5
- 程序源代码
task5.cpp
#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' << std::endl;
}
}
std::string dec2n(int x, int n) {
if (x == 0) {
return "0";
}
std::string table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//字符串赋值
std::string res;
while (x > 0) {
int rem = x % n;
res += table[rem];//字符串可以通过通过下标找到
x = x / n;
}
std::reverse(res.begin(), res.end());
return res;
}
- 运行截图

任务6
- 程序源代码
task6.cpp
#include<iostream>
#include<string>
using namespace std;
void printTable() {
cout << " ";
for (char ch = 'a'; ch <= 'z'; ch++)
cout << ch << " ";
cout << "\n";
for (int i = 1; i <= 26; i++) {
if (i < 10)
cout <<" "<< i << " ";
else
cout << i << " ";
for (int j = 0; j < 26; j++) {
char ch = 'A' + (i - 1 + j) % 26;
cout << ch << " ";
}
cout << endl;
}
}
int main() {
printTable();
}
- 运行截图

任务7
- 程序源代码
task7.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include<iomanip>
using namespace std;
int main()
{
srand((unsigned)time(NULL));
const int total = 10;
int correct = 0;
for (int i = 0; i < total; ++i)
{
int op = rand() % 4;
int a, b, std_ans;
char op_ch;
if (op == 0)
{
op_ch = '+';
a = rand() % 10 + 1;
b = rand() % 10 + 1;
std_ans = a + b;
}
else if (op == 1)
{
op_ch = '-';
do
{
a = rand() % 10 + 1;
b = rand() % 10 + 1;
} while (a < b);
std_ans = a - b;
}
else if (op == 2)
{
op_ch = '*';
a = rand() % 10 + 1;
b = rand() % 10 + 1;
std_ans = a * b;
}
else
{
op_ch = '/';
do
{
a = rand() % 10 + 1;
b = rand() % 10 + 1;
} while (a % b != 0);
std_ans = a / b;
}
int user_ans;
cout << a << " " << op_ch << " " << b << " = ";
cin >> user_ans;
if (user_ans == std_ans)
{
correct++;
}
}
double rate = 100.0 * correct / total;
cout << "正确率:" << fixed<<setprecision(2) <<rate<< "%" << endl;
return 0;
}
- 运行截图
![image]()


浙公网安备 33010602011771号