C++学习笔记——泛函算法

泛型算法

泛型算法基础知识

  • 所在头文件
#include <algorithm>  //基本算法

#include <numeric>  //数值算法

#include <ranges>  //C++20引入
  • 方法和泛型算法同名时优先使用方法,泛型算法为满足通用性损失了一部分性能
  • 迭代器作为不同数据类型沟通的桥梁 std::sort(std::begin(x), std::end(x))

泛函算法分类

  • 读算法 给定迭代空间,读取其中元素并运算,不会修改迭代空间
T = std::accumulate(it1, it2, T init, Binary Operation op)  //op二元运算符,默认为+,可lambda表达式

std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 
int sum = std::accumulate(v.begin(), v.end(), 0);
 
int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
 
auto dash_fold = [](std::string a, int b) {
                     return std::move(a) + '-' + std::to_string(b);
                     };
 
std::string s = std::accumulate(std::next(v.begin()), v.end(),
                                    std::to_string(v[0]), // start with first element
                                    dash_fold);
 
    // Right fold using reverse iterators
std::string rs = std::accumulate(std::next(v.rbegin()), v.rend(),
                                     std::to_string(v.back()), // start with last element
                                     dash_fold);
 
std::cout << "sum: " << sum << '\n'
          << "product: " << product << '\n'
          << "dash-separated string: " << s << '\n'
          << "dash-separated string (right-folded): " << rs << '\n';

It find(it1, it2, const T& value);
  • 写算法
void fill(it1, it2, const T& value); 将多个元素赋值为value
void fill_n(it, size_t count, const T& value); 赋值n个value
  • 读+写
outit transform(it1, it2, outit1, unary operation);  //it1--it2 读区间  outit1 写入区间开头 outit指向转换后的最后一个元素

outit copy(it1, it2, outit1);

//删除重复元素
sort(it1, it2)
it unique(it1, it2)
//输入122334 转换后为123444
//输出it指向重复元素的开头 例如此时指向最后第二个4

迭代器分类

头文件 #include

  • 输入迭代器,可读可递增 InputIt 典型应用find
  • 输出迭代器,可写可递增 OutputIt 典型应用copy
  • 前向迭代器,可读写可递增 ForwardIt 典型应用replace
  • 双向迭代器,可读写可递增递减 BirdirIt 典型应用reverse
  • 随机访问迭代器,可读写可增减一个整数 RandomIt 典型应用sort
  • 特殊迭代器
    • back_insert_ierator front_insert_iterator
    #include <iterator>
    #include <deque>
    
    int main()
    {
        std::deque<int> a;
        std::back_insert_iterator<std::deque<int>> it(a);
        it = 1;
        it = 2;  //调用push_back在结尾插入
        std::fill_n(it, 10, 3);
    }
    
    • istream_iterator ostream_iterator
    #include <iterator>
    #include <sstream>
    
    std::istringstream str("1, 2, 3, 4");
    std::istream_iterator<int> x(str);
    std::istream_iterator<int> y{};  //用于表示结尾
    for(; x != y; ++x)
    {
        std::cout<<*x<<std::endl;  //从第一个int开始寻找到下一个int,需手动++x;才后移
    }
    
    std::copy(std::istream_iterator<int>(str),
              std::istream_iterator{},
              std::ostream_iterator<int>(std::cout, " "));  //绑定的输出流和分割符
    
    • 反向迭代器 rbegin rend
    • move_iterator
    #include <vector>
    #include <algorithm>
    #include <string>
    #include <iterator>
    int main()
    {
        std::vector<std::string> v{"this", "_", "is"};
        std::string concat = std::accumulate(std::make_move_iterator(v.begin()),
                             std::make_move_iterator(v.end()),
                             std::string());
        // concat this_is
    }
    
    • 哨兵(sentinel)

并发算法C++17

#include <execution>

incline constexpr std::execution::sequenced_policy seq{};  //顺序执行
incline constexpr std::execution::parallel_policy par{};  //并行执行
incline constexpr std::execution::parallel_unsequenced_policy par_unseq{};
incline constexpr std::execution::unsequenced_policy unseq{};

应用

#include <execution>

std::vector<double> vals(10000);
for(auto& d : vals)
{
    d = static_cast<double>(rd());
}
for(int i = 0; i < 5; i++)
{
    using namespace std::chrono;
    syd::vector<double> sorted(vals);
    const auto startTime = high_resolution_clock::now();
    std::sort(std::execution::par, sorted.begin(), sorted.end());
    const auto endTime = high_resolution_clock::now();
    std::cout << duration_cast<duration<double, std::milli>>(endTime - startTime).count() << std::endl;
}

bind

泛型算法允许通过可调用对象自定义计算逻辑细节如:

transform(InIt1, InIt2, Out1, unary operation op)

outIt copy_if(InIt1, InIt2, Out It, unary predicate pred)

sort(RandomIt1, RandomIt2, compare)

bool MyPredict(int val)
{
    return val > 3;
}

int main()
{
    std::vector<int> x{1, 2, 3, 4, 5, 6};
    std::vector<int> y;
    syd::copy_if(x.begin(), x.end(), std::back_inserter(y), MyPredict);  //456
}

可调用对象:

  • 函数指针:直观,但定义位置受限,不能在函数中定义函数
  • 类:功能强大,但书写麻烦
  • bind:逻辑复杂时难懂
  • lambda:厉害

bind应用

bind1st bind2st

#include <functional>
#include <algorithm>
std::vector<int> x{1, 2, 3, 4, 5, 6};
std::copy_if(x.begin(), x.end(), std::back_inserter(y), std::bind1st(std::greater<int>(), 3));
//3绑定greater的第一个参数 复制后为12

std::copy_if(x.begin(), x.end(), std::back_inserter(y), std::bind1st(std::greater<int>(), 3));
//3绑定greater的第二个参数 复制后为456

bind

template <typename T>
bool Mypredict(T a, T b)
{
    return a > b;
    
}
int main()
{
    std::vector<int> x{1, 2, 3, 4, 5, 6};
    std::vector<int> y;
    using namespace std::placeholders;
    std::copy_if(x.begin(), x.end(), std::back_inserter(y), std::bind(Mypredict, _1, 3));  //复制后为456
    (_1,3)表示调用时Mypredict函数的参数列表 即Mypredict(each_x,3)
    这里的_1中的1表示调用bind时传入的第一个参数
    例如:auto x = std::bind(Mypredict, _2, _1);
    x(3, 4) //true 3替换_1  4替换_2 Mypredict(4,3) 因此返回true
}

注意:构造bind时会将参数赋值

void proc(int& x)
{
    ++x;
}
int x = 0;
auto b = std::bind(proc, x); //传入的x会被复制一份再传入proc
b();
std::cout<<x;  //还是0

如果实在想传入引用,可以auto b = std::bind(proc, std::ref(x)); 或cref(x)

lambda表达式

底层翻译成类来实现

定义

auto x = [](int val){return val>3;};  //自动推导出返回类型
x(5)  //true

auto x = [](int val){return val*val;); -> double  //显示指定返回类型

捕获

  • 针对局部对象的捕获
int y = 10;
auto x = [y](int val){return val > y;};
  • 局部静态对象无需捕获
static int y = 10;
auto x = [](int val){return val > y;};
  • 值捕获
int y = 10;
auto x = [y](int val) mutable {++y; return val > y;};
x(5) //false
y  //10没有改变
  • 引用捕获
auto x = [&y](int val) {++y; return val > y;};
x(5)  //false
y  //11改变了
  • 值和引用同时用
auto x = [&y, z](int val) {...};
  • 自动值捕获
auto x = [=](int val) {...};
  • 自动引用捕获
auto x = [&](int val) {...};
  • 自动+值捕获
auto x = [&, y](int val) {...};
  • *this捕获
struct str
{
    auto fun()
    {
        int val = 3;
        auto lam = [val, *this]()  //使用*this会将对象内容保存至lam内部
        {
            return val > x;
        };
        return lam();
    }
    
    int x;
};
  • 初始化捕获 C++14
int x = 3;
auto lam = [y = x](int val)
{
    return val > y;
};

std::string a = "hello";
auto lam = [y = std::move(a)]()
{
    std::cout << y;
};

lambda其他基础知识

  • mutable说明符
    默认为const,不能改变‘类’内的值
int y = 3;
auto lam = [](int val) constexpr
{
    ++y;
    return val > y;
};
  • constexpr说明符 C++17
auto lam = [](int val) constexpr
{
    return val + 1;
}
constexpr int val = lam(100);
  • 带模板参数 C++20
auto lam = []<typename T>(T val)
{
    return val + 1;
};

lambda高级使用

  • IIFE 立即调用函数表达式
const auto val = [z = x + y](){return z;}();
  • C++14 自动引入模板
auto lam = [](auto x){return x + 1;};
  • 递归调用
普通函数为什么能递归调用:由于函数对象在执行到函数体之前就已经建立好了,因此不会报错。但lambda表达式不同,其还没有构造好。

auto factorial = [](int n)
{
    auto f_imp1 = [](int n, const auto& imp1) ->int
    {
        return n > 1 ? n*imp1(n-1, imp1) : 1;
    };
    return f_imp1(n, f_imp1);
};
为什么可以?
factorial内部没有factorial,f_imp1内部没有f_imp1
f_imp1定义时利用C++14中的const auto&定义了一个模板,在调用f_impl时实例化
不指定->int会报错,因为不知道f_imp1返回值类型(用了auto,需要从return语句来推断),而return语句中有imp1,就得知道imp1的类型,要知道imp1类型就得知道f_imp1类型,反反复复,最终还是不知道,报错。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值