C++(10):泛型算法

news2024/12/23 6:05:48

泛型算法:可用于不同类型的容器和不同类型的元素的通用算法。

概述

大多数算法都定义在头文件algorithm 中。标准库在头文件 numeric 中定义了一组数值泛型算法。
一般情况下,泛型算法不直接操作容器,而是遍历由两个迭代器指定的一个元素范围来进行操作。通常情况下,算法遍历范围,对其中每个元素进行一些处理。

迭代器令算法不依赖于容器,但算法依赖于元素类型的操作。

算法不会改变容器的大小,只能改变其内的元素值以及移动元素。

初识泛型算法

标准库提供了超过 100 个算法。除了少数例外,标准库算法都对一个范围内的元素进行操作,此元素范围称为 “输入范围”。接受输入范围的算法总是使用前两个参数来表示此范围,两个参数分别是指向要处理的第一个元素和尾元素之后位置的迭代器。

只读算法

只读取其输入范围内的元素,而从不改变元素。通常最好使用 cbegin()cend()
常见的三种只读算法:

int sum = accumulate(vec.cbegin(),vec.cend(),val);//对 vec 中的元素在 val 的基础上求和。accumulate 的第三个参数的类型决定了函数中使用哪个加法运算符以及返回值的类型。
bool F = equal(vec1.cbegin(),vec1,end(),vec2.cbegin());//比较两个序列的元素是否全部相等(第二个序列至少与第一个序列一样长)
auto iter = find_if(vec.begin(),vec.end(),'一元谓词');//查找符合某条件的元素,返回迭代器,如果没有找到就返回尾迭代器

写容器元素的算法

使用赋值序列中的元素的算法时,必须确保序列原大小至少不小于算法要写入的元素数目。

//算法fill接受一对迭代器表示一个范围,还接受一个值作为第三个参数
fill(vec.begin(), vec.end()0);//将每个元素重置为0
fill(vec.begin(), vec.begin() + vec.size()/210);//将容器的一个子序列设置为10
fill_n(vec.begin(), vec.size(),0);//将所有元素重置为0
fill_n(dest,n,val);//将 val 赋予从 dest 开始的连续 n 个元素。假定dest开始的序列有至少 n 个元素

插入迭代器

插入迭代器是一种向容器中添加元素的迭代器。
通常情况下,通过一个迭代器向容器元素赋值时,值被赋予迭代器指向的元素。通过一个插入迭代器赋值时,一个与赋值号右侧值相等的元素被添加到容器中。

back_inserter:定义在头文件 iterator 中。
back_inserter 接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。通过此迭代器赋值时,赋值运算符会调用push_back将一个具有给定值的元素添加到容器中:

vector<int> vec; //空向量
auto it = back_inserter(vec);//通过它赋值会将元素添加到vec中
*it =42;// vec 中现在有一个元素,值为42

使用back inserter来创建一个迭代器,作为算法的目的位置来使用:

vector<int> vec; //空向量
//正确:back inserter创建一个插入迭代器,可用来向vec添加元素
fill_n(back inserter(vec)100);//添加10个元素到vec

在每步迭代中,fill_n 向给定序列的一个元素赋值。由于传递的参数是 back_inserter 返回的迭代器,因此每次赋值都会在 vec 上调用 push_back 。最终,这条 fill_n 调用语句向 vec 的末尾添加了10个元素,每个元素的值都是0。

拷贝算法

拷贝(copy)算法是另一个向目的位置迭代器指向的输出序列中的元素写入数据的算法。
此算法接受三个迭代器,前两个表示一个输入范围,第三个表示目的序列的起始位置。此算法将输入范围中的元素拷贝到目的序列中。传递给copy的目的序列至少要包含与输入序列一样多的元素。

int a1[] = {0,1,2,3,4,5,6,7,8,91;
int a2[sizeof(a1)/sizeof(*a1)];//a2与al大小一样
//ret指向拷贝到a2的尾元素之后的位置
auto ret = copy(begin(al),end (al),a2);//把a1的内容拷贝给a2

replace 算法读入一个序列,并将其所有等于给定值的元素都改为另一个值。

replace(ilst.begin(),ilst.end(),0,42);//把输入范围内所有值为 0 的元素改为 42
replace_copy(ilst.begin(),ilst.end(),dest,0,42);//把输入范围内所有值为 0 的元素改为 42 并拷给 dest 开头的序列。

重排容器元素的算法

调用sort()函数会重新输入序列中的元素,使之变得有序。

sort(words.begin(),words.end());//默认按升序从小到大排列输入范围内的元素

调用unique()来使得重复的元素放在序列的后端,并且返回指向序列后端重复元素中的第一个重复元素的迭代器。

auto end_unique = unique(words.begin(),words.end());

调用erase()删除元素。

words.erase(end_unique,words.end());//erase()函数可以接受两个相等的迭代器作为参数。删除一个空范围没有什么后果。

定制操作

允许使用自定义操作符来替代默认运算符。

向算法传递函数

谓词:是一个可调用的表达式,其结果是作为一个能用作条件的值。
标准库使用的谓词分为两类:一元谓词(只接受单一参数)和二元谓词(有两个参数)。
接受谓词参数的算法对输入序列中的元素调用谓词。因此,元素类型必须转换为谓词的参数类型。

lambda 表达式

一个lambda表达式表示一个可调用的代码单元。可以将其理解为一个为命名的内联函数。
Lambda 表达式如下:

[capture list](parameter list) -> return type {function body}
//lambda:捕获列表、参数列表、返回类型、函数体

与函数唯一的不同就是:1.lambda 可能定义在函数内部;2.lambda 必须使用尾置返回来指定返回类型。

可以忽略参数列表和返回类型,但必须包含捕获列表和函数体

auto f = [] {return 42}//定义了一个可调用对象 f,返回 42

如果 lambda 的函数体包含任何单一 return 语句之外的内容,且未指定返回类型,则返回void。

lambda表达式的调用方式也是使用调用运算符,内含实参。一个 Lambda 调用的实参数目永远与形参数目相等。因为,Lambda 不能有默认参数。

一个lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该变量。

//错误:sz未捕获
[](const string &a){
	return a.size()>=sz;
};

[sz](const string &a){
	return a.size()>=Sz;
};

捕获列表只用于局部非 static 变量,lambda 可以直接使用局部 static 变量和在它所在函数之外声明的名字。

lambda 捕获和返回

当定义一个 lambda时,编译器生成一个与 lambda对应的新的(未命名的)类类型。
当向一个函数传递一个 lambda 时,同时定义了一个新类型和该类型的一个对象:传递的参数就是此编译器生成的类类型的未命名对象。
当使用 auto 定义一个用 lambda 初始化的变量时,定义了一个从 lambda 生成的类型的对象
默认情况下,从 lambda生成的类都包含一个对应该lambda所捕获的变量的数据成员。

变量的捕获方式也可以是值或引用。

lambda 捕获列表
在这里插入图片描述

值捕获

采用值捕获的前提是变量可以拷贝。

void fcnl(){
	size_t vl=42;//局部变量
	//将v1拷贝到名为f的可调用对象
	auto f= [v1] { return vl; };
	v1 =0;
	auto j=f();//j为42;f保存了我们创建它时v1的拷贝
}

被值捕获的变量的值是在 lambda 创建时拷贝,而不是调用时拷贝。因此,在 lambda 创建后改变被捕获的变量不会影响 lambda 中对应的值。

引用捕获

采用引用捕获,要确保被引用的对象在 lambda 执行的时候是存在的。

void fcn2(){
	size_t v1=42;//局部变量
	//对象f2包含v1的引用
	auto f2=[&v1] { return vl; };//v1之前的&指出v1应该以引用方式捕获。
	v1=0;
	autoj=f2();//j为0;f2保存v1的引用,而非拷贝
}

在 lambda 函数体内使用以引用方式捕获的变量时,实际上使用的是引用所绑定的对象。
lambda 捕获的都是局部变量。

隐式捕获

可以使编译器根据 lambda 体中的代码来推断我们要使用哪些变量:&告诉编译器采用捕获引用方式,=则表示采用值捕获方式。

//sz为隐式捕获,值捕获方式
wC = find_if(words.begin() , words.end(),
	[=](const string &s)
		{return s.size()>=sz;});

希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显式捕获

void biggies(vector<string> &words,
	vector<string>::size_type sz,
	ostream &os = cout, char c=' '){
	//其他处理与前例一样
	//os 隐式捕获,引用捕获方式,c显式捕获,值捕获方式
	for_each(words.begin(),words.end(),
		[&,c](const string &s){os << s << c;});
	//os 显式捕获,引用捕获方式,c阴式捕获,值捕获方式
	for_each(words.begin(),words.end(),
		[=,&os](const string &s){os << s << c;});

可以通过混合使用隐式捕获和显示捕获来实现对一部分变量使用值传递,而另一部分使用引用传递。
混合捕获时捕获列表中的第一个元素必须是一个 &=

  1. 如果是 &,表示采用隐式的引用捕获,此时后面显式捕获的变量必须都采用值捕获。
  2. 如果是 =,表示采用隐式的值捕获,此时后面显式捕获的变量必须都采用引用捕获。

可变 lambda

默认情况下,通过值捕获拷贝的变量,lambda 不会改变其值。如果改变一个被捕获的变量的值,必须在参数列表首加上关键字 mutable

void fcn3(){
	size_t v1=42;//局部变量
	//f可以改变它所捕获的变量的值
	auto f = [v1]() mutable { return ++v1; };
	v1=0;
	auto j= f();//j为43
}

一个引用捕获的变量是否(如往常一样)可以修改依赖于此引用指向的是一个const类型还是一个非const类型。

指定 lambda 返回类型

默认情况下,如果一个 lambda 体包含 return 之外的任何语句,则编译器假定此 lambda 返回 void。与其他返回 void 的函数类似,被推断返回 void 的 lambda不能返回值。

如果 lambda 中只包含一个单一的 return 语句,可以省略返回类型。
如果 lambda 中除了 return 还有其他语句,此时应该指明返回类型。

需要为一个lambda定义返回类型时,必须使用尾置返回类型。

参数绑定

lambda 表达式适合那种只在一两个地方使用的简单操作。如果操作需要很多语句或需要在很多地方使用,通常应该定义一个函数。
对于捕获列表为空的 lambda,通常可以用函数来代替。

标准库 bind 函数

调用 bind 的一般形式为:

//newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。
auto newCallable bind(callable,arg _list);

bind 定义在头文件 functional 中。可以将bind函数看作一个通月的函数适配器,它接受一个可调用对象,生成一个新的可调用象来"适应"原对象的参数列表。

bind 占位符

arg_list 中的参数可能包含形如 n 的名字,其中 n 是一个整数。这些参数是 “占位符”,表示 newCallable 的参数,它们占据了传递给 newCallable 的参数的“位置”。数值 n 表示生成的可调用对象中参数的位置:_1 为 newCallable 的第一个参数,_2 为第二个参数,以此类推。

名字_n 都定义在一个名为 placeholders 的命名空间中,而这个命名空间本身定义在std命名空间中。
声明语句为:using std::placeholders::_n;

bind 的参数

1.可以用 bind 修正参数的值,固定某些参数。

//假定 f 是一个可调用对象,有 5 个参数
// g 是一个有两个参数的可调用对象
auto g = bind(f,a,b,_2,c,_1)
//生成一个新的可调用对象,它有两个参数,分别用占位符_2和_1表示。

2.可以用 bind 绑定给定可调用对象中的参数或重新安排其顺序。

// 用 bind 颠倒 isShroter 的含义
//按单词长度由短至长排序
sort(words.begin(),words.end(), isShorter);
//按单词长度由长至短排序
sort(words.begin(),words.end()bind (isShorter,_2,_1));

绑定引用参数

默认情况下,bind 的那些不是占位符的参数是值传递,被拷贝到 bind 返回的可调用对象中。
如果要传递引用或该参数类型不能拷贝(ostream),可以使用 ref 函数:

for_each (words.begin(), words.end(),
	bind(print, ref(os), 1, ' '));

函数ref返回一个对象,包含给定的引用,此对象是可以拷贝的。
cref 可以生成保存 const 引用的类。refcref 也定义在头文件 functional 中。

再探迭代器

  • 插入迭代器(insert iterator):迭代器被绑定到一个容器上,可用来向容器插入元素。
  • 流迭代器(stream iterator):迭代器被绑定到输入或输出流上,可用来遍历所关联的IO流。
  • 反向迭代器(reverse iterator):迭代器向后而不是向前移动。除了forward list之外的标准库容器都有反向迭代器。
  • 移动迭代器(move iterator):这些专用的迭代器不是拷贝其中的元素,而是移动它们。

插入迭代器

插入器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素。
通过一个插入迭代器进行赋值时,该迭代器调用容器操作来向给定容器的指定位置插入一个元素。
插入迭代器支持的操作:

it = t //在it指定的当前位置插入值t。假定c是it绑定的容器,依赖于插入迭代器的不同种类,此赋值会分别调用c.push_back(t)、c.push_front(t) 或 c.insert(t,p),其中p为传递给 inserter 的迭代器位置
*it,++it,it++ //这些操作虽然存在,但不会对it做任何事情。每个操作都返回it

插入器有三种类型,差异在于元素插入的位置:
1.back_inserter 创建一个使用 push_back 的迭代器。
2.front_inserter 创建一个使用 push_front 的迭代器。
3.inserter 创建一个使用 insert 的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。

iostream 迭代器

IO 类型对象的迭代器:istream_iterator 读取输入流,ostream_iterator 向一个输出流写数据。这些迭代器将对应的流当作一个特定类型的元素序列来处理。
创建一个流迭代器时,必须指定迭代器将要读写的对象类型。

istream_iterator 操作

一个 istream_iterator 使用 >> 来读取流。istream_iterator要读取的类型必须定义了输入运算符。
在这里插入图片描述

ostream_iterator 操作

ostream_iterator 可以对任何具有输出运算符(<<运算符)的类型定义。其赋值操作等价于输出流的输出操作。每赋一次值,输出一次。
在这里插入图片描述

反向迭代器

反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。 对于反向迭代器,递增(以及递减)操作的含义会颠倒过来。递增一个反向迭代器(++it)会移动到前一个元素;递减一个迭代器(--it)会移动到下一个元素。

除了 forward_list 外,其他容器都支持反向迭代器。
可以通过调用 rbeginrendcrbegincrend 成员函数来获得反向迭代器。这些成员函数返回指向容器尾元素和首元素之前的一个位置。
在这里插入图片描述

反向迭代器支持递增和递减操作。注意流迭代器不支持递减操作,因为不可能在一个流中反向移动。
反向迭代颠倒:reverse_iteratorbase 成员函数会返回相应的正向迭代器,正向迭代器指向靠后一个元素。

//正确:得到一个正向迭代器,从逗号开始读取字符直到line末尾
// rcomma 是一个反向迭代器
cout<<string(rcomma.base(), line.cend())<<endl;

泛型算法结构

迭代器类别

在这里插入图片描述

输入迭代器
1.用于比较两个迭代器的相等和不相等运算符(=!=
2.用于推进迭代器的前置和后置递增运算(++
3.用于读取元素的解引用运算符(*);解引用只会出现在赋值运算符的右侧
4.箭头运算符(->),等价于(*it).member,即,解引用迭代器,并提取对象的成员

输出迭代器
1.用于推进迭代器的前置和后置递增运算(++
2.解引用运算符(*),只出现在赋值运算符的左侧(向一个已经解引用的输出迭代器赋值,就是将值写入它所指向的元素)

前向迭代器
1.只能在序列中沿一个方向移动。
2.支持所有输入和输出迭代器的操作,可以多次读写同一个元素。
3.算法 replace 要求前向迭代器,forward_list 上的迭代器是前向迭代器。

双向迭代器
1.可以正向/反向读写序列中的元素。
2.支持所有迭代器操作,还支持前置和后置递减运算符(--
3.算法 reverse 要求双向迭代器。

随机访问迭代器
1.用于比较两个迭代器相对位置的关系运算符(<<=>>=
2.迭代器和一个整数值的加减运算(+=--=),计算结果是迭代器在序列中前进(或后退)给定整数个元素后的位置
3.用于两个迭代器上的减法运算符(-)
4.得到两个迭代器的距离下标运算符(iter[n]),与*(iter[n])等价

算法形参模式

算法的形参一般是以下 4 种模式之一

  • alg(beg, end, other args);
  • alg(beg, end, dest, other args); dest 经常是一个插入迭代器或一个 ostream_iterator,他们都能确保不管写多少元素都肯定是安全的;
  • alg(beg, end, beg2, other args);
  • alg(beg, end, beg2, end2, other args);

alg 是算法的名字,beg 和 end 表示算法所操作的输入范围。

算法命名规范

除了参数规范,算法还遵循一套命名和重载规范。

重载谓词的算法
一些算法使用重载形式传递一个谓词,来代替 <==

unique(beg, end);          // 使用 == 比较元素
unique(beg, end, comp);    // 使用 comp 比较元素

_if 版本的算法
接受谓词参数的算法都有附加的 if 前缀

find(beg, end, val);       // 查找范围内 val 第一次出现的版本。
find_if(beg, end, pred);   // 查找第一个令 pred 为真的元素。

_if 版本的算法不是重载算法。

拷贝版本的算法
拷贝版本将元素写到一个给定的位置。

reverse (beg, end);//反转输入范围中元素的顺序
reverse_copy (beg, end, dest);//将元素按逆序考贝到dest

特定容器算法

链表类型 listforward_list 定义了几个成员函数形式的算法。定义了独有的 sort, merge, remove, reverse , unique
通用版本的 sort 要求随机访问迭代器,因此不能用于listforward_list

其他链表类型定义的算法的通用版本可以用于链表,但是性能差很多,因为这些算法需要交换输入序列中的元素
优先使用成员函数版本算法

listforward_list 成员函数版本的算法

lst.merge(lst2);// 将 lst2 的元素合并入 lst。两者必须都是有序的,元素将从 lst2 删除,合并后。lest2 变为空。使用 < 比较元素或给定操作。
lst.merge(lst2,comp);//给定操作
 
lst.remove(val);        // 调用 erase 删除掉每个元素。
lst.remove_if(pred);    // 删除掉 lst 中使一元谓词 pred 为真的每个元素。
 
lst.reverse();// 反转 lst 中元素的顺序。
 
lst.sort();// 使用 < 排序元素
lst.sort(comp); //给定操作排序元素
 
lst.unique(); // 调用 erase 删除同一个值的连续拷贝。
lst.unique(pred);// pred 是个二元谓词,删除使 pred 为真的连续拷贝。

splice 成员
在这里插入图片描述

重要术语

泛型算法(generic algorithm) 类型无关的算法。
谓词(predicate) 返回可以转换为 bool 类型的值的函数。泛型算法通常用来检测元素。标准库使用的谓词是一元(接受个参数)或二元(接受两个参数)的。

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

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

相关文章

【Spring Boot学习】日志文件,Spring Boot也会写日记了,这些事你知道嘛 ? ? ?

前言: 大家好,我是良辰丫,在上一篇文章中我们已经学习了Spring Boot的配置,接下来我们要学习一些日志相关的东西,什么是日志呢?我们慢慢往下看.&#x1f48c;&#x1f48c;&#x1f48c; &#x1f9d1;个人主页&#xff1a;良辰针不戳 &#x1f4d6;所属专栏&#xff1a;javaE…

7.4_2B树的插入删除

我们先设置根节点 我们再往里面插入关键字 比如说&#xff1a;80 中间位置为49&#xff1a;&#xff08;5/2&#xff09;向上取整为3 新元素一定要插入到最底层”终端结点“&#xff0c;用”查找”来确定插入位置。 失败节点&#xff08;叶子节点不属于同一层&#xff09; 讲…

Vue-全局事件总线(GlobalEventBus)

全局事件总线(GlobalEventBus) 全局事件总线是vue中特别厉害的一种组件中通信的方式&#xff0c;它可以实现任意组件中通信&#xff0c;随便拿出来两个组件就能互通数据&#xff0c;就像对讲机一样&#xff0c;它在开发中用的也是特别的多 1 编写案例 首先准备两个组件&…

Python列表 (超详细举例加讲解)

得之我幸&#xff0c;失之我命 文章目录 1.列表的值 2.列表的定义 3.下标 4.列表长度 5.列表的加法和乘法 6.列表切片 7.操作方法&#xff08;一些基础的函数&#xff09; &#xff08;1&#xff09;append——向列表末尾添加元素 &#xff08;2&#xff09;insert——…

VUE3实现页面缓存,tab切换时不刷新

如上图所示&#xff0c;为了实现页面缓存&#xff0c;防止每次页面切换时重新刷新数据&#xff0c;前前后后尝试了多种写法&#xff0c;如上图被注释的那段写法&#xff0c;与没注释掉的写法&#xff0c;在router-view上主要是第一种写法有设置key属性&#xff0c;第二种没有&a…

常见面试题之框架篇

1.Spring框架中的单例bean是线程安全的吗&#xff1f; 不是线程安全的&#xff0c;是这样的。 当多用户同时请求一个服务时&#xff0c;容器会给每一个请求分配一个线程&#xff0c;这是多个线程会并发执行该请求对应的业务逻辑&#xff08;成员方法&#xff09;&#xff0c;…

模拟电路系列分享-负反馈电路稳定性分析

目录 概要 整体架构流程 技术名词解释 1.负反馈放大电路产生自激震荡的条件 2.从实际运放的幅频&#xff0c;相频特性看自激振荡的可能性 小结 概要 在卡拉0K歌厅中,我们会见到这样一种现象:当麦克风位置不合适或者音量过大时, 喇叭中会出现一种非常难听的啸叫,捂住麦克风、赶紧…

【无需显卡】AI绘画入门教程

前言 Hello&#xff0c;各位端午节快乐呀&#xff01;不好意思拖更两个月&#xff0c;最近实在是太忙了&#xff0c;也想不到有什么好玩的&#xff0c;之前介绍过了几个好玩的ai网站&#xff0c;非常适合新手尝鲜&#xff0c;但很多都有额度限制&#xff0c;而且还开始收费了&…

【C语言】内存你知多少?详解C语言动态内存管理

目录 一&#xff0c; 计算机中的内存 二&#xff0c;动态内存申请函数 2.1 头文件 2.2 malloc函数 2.3 free函数 2.3 calloc函数 2.4 realloc函数——调整空间函数 情况1&#xff1a;原有空间之后有足够大的空间 情况2&#xff1a;原有空间之后没有足够大的空间 2…

Android大图加载优化方案,避免程序OOM

我们在编写Android程序的时候经常要用到许多图片&#xff0c;不同图片总是会有不同的形状、不同的大小&#xff0c;但在大多数情况下&#xff0c;这些图片都会大于我们程序所需要的大小。比如微博长图&#xff0c;海报等等。所以我们就要对图片进行局部显示。 大图加载基本需求…

信号与系统复习笔记——信号与系统的时域和频域特性

信号与系统复习笔记——信号与系统的时域和频域特性 傅里叶变换的模和相位表示 一般来说&#xff0c;傅里叶变换的结果是复数&#xff0c;所以能够使用模和相位来表示&#xff0c;具体的有&#xff1a; X ( j ω ) ∣ X ( j ω ) ∣ e j ∡ X ( j ω ) X(j\omega) |X(j\ome…

浅尝Transformer和LLM

文章目录 TransformerTransformer的衍生BERTPre-trainingBERT与其他方法的关系怎么用BERT做生成式任务&#xff1f; GPTPre-trainingFine-Tuning Transformer工具开源库特点 LLM系列推理服务 大语言模型势不可挡啊。 哲学上来说&#xff0c;语言就是我们的一切&#xff0c;语言…

MySQL 高级(进阶) SQL 语句

创建两个表格 use awsl; create table location (Region char(20),Store_Name char(20)); insert into location values(East,Boston); insert into location values(East,New York); insert into location values(West,Los Angeles); insert into location values(West,Houst…

JMU20 软件工程经济学 复习总结

文章目录 碎碎念0. 基准收益率 i1. 现金流量图2. 净现值 NPV&#xff0c;内部收益率 IRR3. 单利&#xff0c;复利计算4. 等额年金NAV5. 动态回收期 P t ′ P_t Pt′​6. 固定资产折旧 [书P44]7. 增值税8. 软件行业增值税的即征即退9. 利息备付率 ICR&#xff0c;偿债备付率 DSC…

C语言之分支与循环

一、语句 什么是语句 C语言中&#xff0c;由一个分号&#xff08; &#xff1b;&#xff09;隔开的即为一条语句。 这些都是语句&#xff1a; &#xff08; 一行里只有 &#xff1b;的语句&#xff0c;我们称其为 “空语句” &#xff09; int main(void) {printf("hel…

UVM1.2究竟在UVM1.1上做了哪些升级

想必大家平时也没有很注意UVM1.1版本和UVM1.2版本的不同之处&#xff0c;只有在用一些以前UVM1.1能支持的功能&#xff0c;到了UVM1.2却出现编译报错&#xff0c;找不到对应的变量或者函数或者类的时候&#xff0c;才意识到这两个版本的差异。笔者也是遇到了1个打印问题&#x…

利用Django的视图类TemplateView将模板、视图与模板变量方便快速的整合在一起

TemplateView是Django提供的通用视图类之一&#xff0c;它允许您在不编写任何Python代码的情况下将模板与视图关联起来。下面是关于TemplateView类的一些介绍&#xff1a; 渲染模板&#xff1a;TemplateView负责渲染指定的模板并返回生成的HTML响应。您只需提供模板名称或路径即…

华为OD机试真题B卷 Java 实现【删除字符串中出现次数最少的字符】,附详细解题思路

一、题目描述 删除字符串中出现次数最少的字符&#xff0c;如果多个字符出现次数一样则都删除。 二、输入描述 一个字符串。 三、输出描述 删除字符串中出现次数最少的字符&#xff0c;如果多个字符出现次数一样则都删除&#xff0c;如果都被删除 则换为empty。 四、解题…

【安装lnmp实操】

文章目录 一、安装Nginx服务1.安装依赖包2、创建运行用户3、编译安装4、优化路径5、添加 Nginx 系统服务 二、【安装 MySQL 服务】1、安装Mysql环境依赖包2、创建运行用户3、编译安装4、修改mysql 配置文件5、更改mysql安装目录和配置文件的属主属组6、设置路径环境变量7、初始…

MySQL数据库高级SQL语句(图文详解!)

一、MySQL高级语句 1.SELECT 2.DISTINST 3.WHERE 4.AND|OR ​5.IN 6.BETWEEN 7.通配符 8.LIKE 9.ORDER BY 10.函数 &#xff08;1&#xff09;数学函数 &#xff08;2&#xff09;聚合函数 &#xff08;3&#xff09;字符串函数 11.GROUP BY 12.HAVING 13.别名 14.子查询 …