zoukankan      html  css  js  c++  java
  • Tips for C++ Primer Chapter 10 泛型算法

    第10章 泛型算法

    accumulate函数

    accumulate(b, e, val)   返回一对迭代器范围内元素的“和”,第三个参数指定“和”的初值;返回类型与第三个实参的类型一致,而与容器内的元素类型无关

    例如:

    vector<double> v{1.1, 2, 3, 4};
    accumulate(v.cbegin(), v.cend(), 0); //返回值是int类型的10
    accumulate(v.cbegin(), v.cend(), 0.0); //返回值是double类型的10.1

    PS:容器中元素类型不一定是数值类型,只要元素类型定义了+运算符即可;例如:string sum = accumulate(v.cbegin(), v.cend(), string("")) 做的事是将[b,e)范围的string依次连接起来并返回

     

    equal函数

    equal(b1, e1, b2) //c1中[b1,e1)范围的元素依次与c2中[b2, +∞)范围的元素比较

    可以用来比较不同容器类型中的元素,且元素类型也不必一样。只要能用==比较两个元素类型即可。

    PS:equal的一个重要假设:第二个序列至少和第一个序列一样长

    fill函数

    fill(b, e, val)   将val赋值给[b,e)范围的元素

    fill_n(b, n, val)   将val赋值给[b,b+n)范围的n个元素

    注意:写操作需保证目标位置足够大,能容纳要写入的元素。特殊地,不要在空容器上调用写操作的函数。

    back_inserter函数

    back_inserter函数返回一个与容器绑定的插入迭代器(insert iterator),定义在头文件iterator中。

    通过插入迭代器赋值时,会调用push_back将一个具有给定值的元素添加到容器中。

    例如:

      vector<int> vec;

      auto it = back_inserter(vec); //it是一个插入迭代器

      *it = 10; //相当于vec.push_back(10)

    再例如:

      fill_n(back_inserter(vec),  5, 0); //在vec的末尾添加5各元素,每个元素的值都为0

    拷贝算法

    copy(b, e, dest)   将[b,e)范围的元素拷贝至dest指向的位置;返回插入到目的位置中的最后一个元素的后一个位置的迭代器

    例如:可以用copy实现内置数组的拷贝

      int a1[] = {0,1,2};

      int a2[sizeof(a1)/sizeof(*a1)]; //保证a2与a1的空间(至少)一样大

      auto ret = copy(begin(a1), end(a1), a2); //ret指向拷贝到a2的尾元素之后的位置

    repalce与replace函数(略)

    重排容器元素的算法

    sort(b,e)   依据元素类型的<运算符来排序

    unique(b,e)   “删除”(覆盖)相邻重复元素;unique函数不改变容器大小;返回的迭代器指向最后一个不重复元素之后的位置

    PS:若要删除靠后的无用元素,可以这样

      c.erase(unique(c.begin(),c.end()), c.end());

    定制操作

    find_if算法

    find_if函数接受一对迭代器,表示一个范围;find_if的第三个参数是一个一元谓词,find_if对输入序列中的每个元素调用给定的这个谓词,返回第一个使谓词返回非0值的元素的迭代器;如果不存在这样的元素,则返回尾后迭代器。
    例子:查找第一个小于5的元素
    bool is(const int &i)
    {
        if(i<5) return 1;
        return 0;
    }

    auto it = find_if(c.begin(), c.end(), is);
    if(it==v.end())
        cout<<"not found"<<endl;
    else
        cout<<*it<<endl;

    lambda表达式

    一个lambda表达式表示一个可调用的代码单元,可以将其理解为一个未命名的内联函数。
    [captrue list] (parameter list) -> return type { function body }
    captrue list:捕获列表;是一个lambda所在函数中定义的局部变量的列表,通常为空。
    parameter list、return type、function body:与普通函数一样,分别表示参数类型、返回列表、函数体;不同的是,lambda必须使用尾置返回。

    例子:编写一个lambda,接受两个int,返回它们的和

      auto sum = [](int &a, int &b) -> int {return a+b;};
      int x,y;
      cout<<sum(x,y)<<endl;

    注意:可以忽略参数列表和返回类型,但必须包含捕获列表函数体
    注意:如果lambda函数体包含任何单一return语句之外的内容,且未指定return type,则返回void;否则,若函数体只是一个return语句,由return的表达式的类型推断返回类型。

    向lambda传递参数
    与普通函数不同,lambda表达式不能有默认参数

    使用捕获列表
    若lambda要使用它所在函数的(非static)局部变量,则必须在捕获列表中指明该局部变量,多个局部变量以逗号分隔。
    空的捕获列表:表明此lambda不使用它所在函数中的任何(非static)局部变量。
    捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和它所在函数之外声明的名字。

    值捕获

    与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝。

    引用捕获

    在捕获列表中的变量名前加上&,指出该变量以引用方式捕获。

    lambda捕获列表的不同形式

    [ ]   空捕获列表;不使用所在函数中的(非static)变量
    [names]   names是以逗号分隔的名字列表,名字前如果使用了&,则采用引用捕获方式,否则采用值捕获方式
    [&]   隐式捕获列表,采用引用捕获方式
    [=]   隐式捕获列表,采用值捕获方式
    [&, identifer_list]   identifer_list是一个逗号分隔的列表,包含0个或多个来自所在函数的变量,这些变量都采用值捕获方式,而任何隐式捕获的变量都采用引用方式捕获。identifer_list中的名字前不能使用&。
    [=, identifer_list]   identifer_list中的变量都采用引用方式捕获,而任何隐式捕获的变量都采用值捕获方式。identifer_list中的名字不能包含this,且这些名字前必须使用&。

    for_each算法

    for_each(b,e,pred)   对迭代器范围[b,e)的元素依次调用一元谓词函数pred

    可变lambda

    对于一个值捕获的变量,lambda不会改变 其值;若希望改变其值,就必须在参数列表后加上关键字mutable。

    参数绑定:bind函数

    (略)

    再探迭代器

    插入迭代器

    插入迭代器实际上是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素。

    插入迭代器有三种:back_inserter、front_inserter、inserter

    插入迭代器的操作

    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

    一个例子:

      vector<int> v;
      auto inst_it = inserter(v, v.begin());
      *inst_it = 1;
      *inst_it = 2;
      *inst_it = 3;

    最终v中含有三个元素1,2,3

      *inst_it = val;

    其效果相当于

      it = v.insert(it, val); //it指向新加入的元素

      ++it; //递增it使它指向原来的元素

    iostream迭代器

    istream_iterator操作

    istream_iterator<T> in(is)   in从输入流is读取类型为T的值

    istream_iterator<T> eof   eof被定义为空的istream_iterator,可以当做尾后迭代器来使用;对于一个关联到流的迭代器,一旦其关联的流遇到文件尾或IO错误(例如读到非T类型的值),迭代器的值就与尾后迭代器相等

    in1 == in2    in1 != in2   in1和in2必须读取相同的类型才能进行比较;若它们都是尾后迭代器,或者绑定到相同的输入,则两者相等

    *in   返回从流中读取的值

    in->mem   相当于(*in).mem

    ++in   in++   使用绑定的元素类型所定义的>>运算符从输入流读取下一个值;前置版本返回指向递增后迭代器的引用,后置版本返回旧值

    ostream_iterator操作

    创建一个ostream_iterator时,可以提供(可选的)第二参数,它是一个C风格字符串(字符串字面值常量、指向字符数组的指针),在输出每个元素后都将打印此字符串。

    必须将ostream_iterator绑定到一个指定的流,不允许空的或表示尾后位置的ostream_iterator。

    ostream_iterator<T> out(os)   out将类型为T的值写到输出流os中

    ostream_iterator<T> out(os, d)   out将类型为T的值写到输出流os中,每个值后面都输出一个d;d是一个C风格字符串

    out = val   用<<运算符将val写入到out所绑定的ostream中;val的类型需要与out可写的类型兼容

    *out   ++out   out++   这些运算符存在,但不对out做任何事情;每个运算符都返回out

    泛型算法结构

    泛型算法形参模式

    alg(beg, end, other args)

    alg(beg, end, dest, other args)   dest参数是一个表示算法可以写入的目的位置的迭代器;dest可能是一个直接指向容器的迭代器,也可能被绑定到一个插入迭代器,或是一个ostream_iterator

    alg(beg, end, beg2, other args)

    alg(beg, end, beg2, end2, other args)

    特定容器算法

    与其它容器不同,链表类型 list 和 forward_list 定义了几个成员函数形式的算法。

    对于 list 和 forward_list 应该优先使用其成员函数版本的算法而不是通用算法,以达到较低代价。例如:一个链表可以改变元素间的链接而不是真的交换它们的值来“交换”元素,因此这些链表版本的算法的性能优于对应的通用版本。

    list、forward_list成员函数版本的算法

    lst.merge(lst2)   lst.merge(lst2, comp)   将来自lst2的元素合并入lst;lst和lst2都必须是有序的;元素将从lst2中删除,merge操作之后,lst2为空;第一个版本使用<运算符、第二个版本使用给定的比较操作

    lst.remove(val)    lst.remove_if(pred)    调用erase删除掉与给定值val相等或令一元谓词pred为真的每个元素

    lst.reverse()   反转lst中元素的顺序

    lst.sort()   lst.sort(comp)   使用<或给定的比较操作排序元素

    lst.unique()   lst.unique(pred)   调用erase删除相邻重复值;第一个版本使用==、第二个版本使用给定的二元谓词pred

    splice成员

    链表类型还定义了splice算法。

    list、forward_list的splice成员函数的参数

    lst.splice(args) 或 flst.splice_after(args)

    args可以是如下形式:

    (p, lst2)   p是指向lst中元素的迭代器,或者是指向flst首前位置的迭代器;函数将lst2的所有元素移动lst中p之前的位置或是flst中p之后的位置;将元素从lst2中删除;lst2的类型必须与lst或flst相同,且不能是同一个链表

    (p, lst2, p2)   p2是一个指向lst2中位置的有效的迭代器;将p2指向的元素移动到lst中,或将p2之后的元素移动到flst中;lst2可以是与lst或flst相同的链表

    (p, lst2, b, e)   b、e必须表示lst2中的合法范围;将给定范围中的元素从lst2移动到lst或flst;lst2可以是与lst或flst相同的链表,但p不能指向[b,e)范围的元素

  • 相关阅读:
    mysql 8 查询报错(sql_mode=only_full_group_by)
    docker安装mysql8版本后 客户端连接出现client does not support authentication...
    docker常用命令
    查看tomcat日志相关Linux命令
    java项目部署到linux服务器涉及的命令
    ehcache与redis对比
    JS中调用BigDecimal处理金额
    thymeleaf模板 th:href 踩坑
    汇总一些绝对有价值的解决方案,边学习边收集
    spring注解总结,spring注解大全
  • 原文地址:https://www.cnblogs.com/junjie_x/p/7729113.html
Copyright © 2011-2022 走看看