zoukankan      html  css  js  c++  java
  • C/C++基础----泛型算法

    算法不依赖与容器(使用迭代器),但大多数依赖于元素类型。如find需要==运算符,其他算法可能要求支持<运算符。

    算法永远不会执行容器的操作,永远不会改变底层容器的大小(添加或删除元素)。

    accumulate(v.cbegin(), v.cend(), string(“ ”)) 算法累加运算符,第3个参数的类型决定了使用哪个+号运算符。

    equal(v1.cbegin(), v1.cend(), v2.cbegin()),逐个比较两个序列。第二个序列至少与第一个序列一样长。

    容器和元素类型都不必一样,只要支持==符号比较两个元素类型。

    那些只接受单一迭代器来表示第二个序列的算法,都假定第二个序列至少与第一个等长。

    确保算法不会访问第二个序列中不存在的元素是程序员的责任。

    算法不检查写操作面,向目的位置迭代器写入数据的算法

    auto it = back_inserter(vec);//返回插入迭代器,使用它赋值时会调用push_back将元素添加到容器。

    fill_n(it,10,0);  //添加10个元素到vec。

    很多算法提供拷贝版本,返回的是一份拷贝,原序列并未改变。

    sort(words.begin(), words.end());//排序

    auto end_unique = unique(words.begin(), words.end());//去重,返回不重复区域后面一个位置

    words.erase(end_unique,words.end());//算法不能直接增删,调用erase删除多余元素

    即时words没有重复元素,此操作也是安全的

    谓词(predicate)

    可调用的表达式,返回结果是一个能用作条件的值

    用谓词替代算法默认的操作,如定义二元isShorter函数,替代sort默认的<比较元素。

    sort(words.begin(), words.end(), isShorter);

    stable_sort(words.begin(), words.end(), isShorter);//维持相等元素的原有排序

    partition将容器划分成两部分,true的在前。partition_stable 维持原有元素的顺序

    可调用对象

    函数、函数指针、函数对象,lambda表达式(可理解为一个未命名的内联函数)

    [capture list] (parameter list) -> return type {function body}

    其中参数列表和返回类型可以省略,但是捕获列表和函数体必须永远包含。

    忽略参数列表等价于空参数列表

    忽略返回类型,如果函数体只有一个return语句则从表达式的类型判断;如包含其他任意语句,则返回void。

    lambda

    不能有默认参数

    lambda只有在其捕获列表中的捕获一个它所在函数的局部变量,才能再函数体中使用该变量。仅限局部非static变量,局部static和所在函数外声明的变量可以直接使用。

    make_plural(count, “word”, “s”) //区分单复数,count为1则单数

    find_if(words.begin(), words.end(), [sz](const string &a){return a.size() >=sz;});

    for_each(wc,words.end(),[](const string &s){cout<<s<<” ”;});

    排序-去重-删除多余-按长度排序-找到长度大于sz的起点,打印

    值捕获

    前提是变量可拷贝,被捕获的变量的值在lambda创建时拷贝,而不是调用时。

    引用捕获

    需要保证捕获的局部变量的有效性,也可以从函数返回lambda,不能包含局部变量的引用捕获。如果可能,尽量避免捕获指针或引用。

    lambda捕获列表

    []

    空捕获列表

    [names]

    名字列表,默认都是被拷贝

    [&]

    隐式捕获列表,都采用引用捕获

    [=]

    隐式捕获列表,都采用值捕获

    [&,identifier_list]

    除个别值捕获

    [=, identifier_list]

    除个别引用捕获

    可变lambda

    默认不会改变被捕获的变量的值,如希望改变,使用mutable。

    对于只在一两个地方使用的简单操作,lambda表达式是最有用的。

    对于在很多地方使用的相同操作,或者一个操作需要很多语句才能完成,通常定义一个函数更好。

    捕获列表为空的lambda,通常可以用函数替代;

    而对于有捕获变量的,就不那么容易了。会面临谓词参数个数不一致的问题。

    bind函数(函数适配器)

    接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。

    auto newCallable = bind(callable, arg_list);

    arg_list对应给callable的参数。其中可能包含_n形式的名字,表示占据了传递给newCallable参数的第n个位置。

    bool check_size(const string& s, string::size_type sz) {return s.size() >= sz;}

    auto check6 = bind(check_size, _1, 6);//

    bind调用只有一个占位符,便是check6只接收一个参数,占位符出现在第一个arg_list的第一个位置,表示check6此参数对应check_size的第一个参数const string&。

    auto g =bind(f, a, b, _2, c, _1);//f有5个参数,其中_1和_2分别对应g的第1个和第2个参数

    g(_1,_2)映射为f(a, b, _2, c, _1)

    需要包含命名空间using std::placeholders::_1;

    using namespace std::placeholders;

    对于不是占位符的参数,默认是拷贝到bind返回的可调用对象中的,有时候需要用引用方式传递ref(os),cref()生成const引用。

    bind ref cref都在头文件functinal里。

    注:bind1st和bind2nd,分别只能绑定第一个或第二个参数,由于局限性强已经在新标准中弃用。binder1st和binder2nd也类似,只不过他们是模板类需要指定op的类型

    bind1st(op, value)(x)  ->  op(value,x)

    bind2nd(op,value)(x)  ->  op(x,value)

    其他迭代器

    插入迭代器:绑定到容器上,可以向容器插入元素

    流迭代器:绑定到输入输出流,可以遍历关联的IO流,只要定义了<<或>>运算符的对象

    反向迭代器:向后移动,forward_list没有

    移动迭代器:不是拷贝其中的元素,而是移动move它们。

    插入迭代器

    *it, ++it, it++ 不会对it做任何事情,返回it

    back_inserter          push_back

    front_inserter         push_front

    inserter                      insert

    如inserter生成的迭代器做如下赋值操作 *it = val;

    效果相当于以下代码

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

    ++it;

    使用不同的插入迭代器,最后生成的序列顺序会不同。

    流迭代器

    创建时必须制定将要读写的对象类型,可以在创建时将其绑定到一个流上,也可以默认初始化迭代器当尾后迭代器使用。

    istream_iterator<int> in_iter(cin);

    istream_iterator<int> eof;

    while(in_iter!=eof)

    vec.push_back(*in_iter++);

    可以改成成如下形式

    istream_iterator<int> in_iter(cin), eof;

    vector<int> vec(in_iter, eof); //从迭代器范围构造vec

    从流中读取的数据来构造vec

    与某些算法结合,来处理流数据。输入流迭代器可以作为源迭代器,输入作为目标迭代器

    如cout<<accumulate(in, eof, 0)<<endl;

    istream_iterator允许懒惰求值,推迟中流中读取数据。标准库保证在第一次解引用前,从流中读取数据的操作已经完成。

    如果迭代器可能销毁,或者两个对象同步使用一个流,那么何时读取就很重要了。

    ostream_iterator允许第二个参数,是C风格的字符串,在输出每个元素之后都会打印此字符串。必须绑定到一个流。

    *out, ++out, out++提供形式支持,不会做任何事情

    ostream_iterator<int> out_iter (cout, “ “);

    for (auto e : vec)

             *out_iter++ = e;

    cout<<endl;

    其中*和++都可以忽略,但是推荐这种写法,可以保持与其他迭代器的使用保持一致,修改容易,而且对读者来说也更为清晰。

    反向迭代器

    使我们可以使用算法透明地向前或向后处理容器。

    需要既支持++也支持--,forward_list和流迭代器不能创建。

    当使用反向迭代器,在打印的时候会反向,需要使用base成员来转换。

    [line.crbegin(), rcomma)和[rcomma.base(), line.end())指向line中相同的元素范围。

    另外反向迭代器表示的范围是不对称的,当从普通迭代器初始化反向迭代器或是给其赋值的时候,其指向并不是相同的。

    5种迭代器类型

    迭代器类别

    输入迭代器

    只读,不写;单遍扫描,只能递增

    输出迭代器

    只写,不读;单遍扫描,只能递增

    前向迭代器

    可读写;多遍扫描,只能递增

    双向迭代器

    可读写;多遍扫描,可递增递减

    随机访问迭代器

    可读写;多遍扫描,支持全部迭代器运算

    算法指明了迭代器参数的最小类别,如传递的迭代器参数基本更低会产生错误。

    输入迭代器:

    至少支持==, !=, 前后置++, 解引用*(赋值的右侧), ->

    *it++保证是有效的,但是递增可能导致其他迭代器失效,不能保证输入迭代器的状态可以保存下来并用来访问元素。因此只能用于单遍扫描算法。

    如find和accumulate支持istream_iterator

    输出迭代器:(可看做输入迭代器的补集)

    至少支持前后置++, 解引用*(赋值的左侧),只能向一个输出迭代器赋值一次,单遍扫描。

    如copy第3个参数,ostream_iterator

    前向迭代器:

    replace, forward_list

    双向迭代器:(--)

    reverse

    随机访问迭代器:

    < <= > >= + += - -= 两个迭代器相减 下标运算符

    sort          array deque string vector 内置数组元素的指针

    常见算法形参类型

    alg(beg, end, other args);

    alg(beg, end, dest, other args);

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

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

    向输出迭代器接入数据的算法都是假定目标空间足够。

    单个beg2的算法假定从beg2开始的序列至少与序列1一样大。

    算法命名规范

    重载传递一个谓词

    _if版本 将接受一个元素值版本改成接受一个谓词版本,使用不同的函数名避免可能的重载歧义。

    copy版本

    默认重排元素的算法写回输入序列,copy版本写到一个指定的输出目的位置。

    链表类型特有算法

    其他通用算法也可以用于链表,但是代价过高,需要交换元素。链表可以通过改变链接来完成交换,而不用真正交换元素。所以优先使用成员函数版本。

    list forward_list成员函数版本算法(都返回void)

    lst.merge(lst2)

    合并,两个都必须是有序的,合并之后lst2为空

    lst.merge(lst2,comp)

    同上,使用谓词给定的比较操作

    lst.remove(val)

    erase删除元素

    lst.remove_if(pred)

    同上

    lst.reverse()

    反转序列

    lst.sort()

    使用<或给定比较操作排序

    lst.sort(comp)

    lst.unique()

    删除同一个值得连续拷贝

    lst.unique(pred)

    同上,使用二元谓词

    splice成员

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

    (p, lst2)  将lst2中所有元素移动到lst中p之前或flst中p之后,元素将从lst2删除,不能为同一个链表

    (p, lst2, p2) 将p2指向的元素移动到lst中,可以是相同的链表

    (p, lst2, b, e) 将b和e之间指向的元素移动到lst中,可以是相同的链表,可以是同链表但不能包含p。

    链表特有版本一个重要的区别是会改变底层的容器。

  • 相关阅读:
    RMI笔记
    java 本地方法(JNI)
    java 的SPI机制
    eclipse中的 Compiler compliance level含义
    初步理解JNDI
    大数据5.1
    大数据4.1
    需要攻破的知识点
    大数据4.2 -- hive数据库
    大数据---单词释义
  • 原文地址:https://www.cnblogs.com/logchen/p/9576388.html
Copyright © 2011-2022 走看看