【C++11】lambda匿名函数和包装器

news2025/3/17 20:51:31

目录

一,lambda匿名函数

1-1,lambda的引入

1-2,lambda表达式书写格式

1-3,lambda函数的名称

1-4,lambda捕获列表的使用

1-5,函数对象与lambda表达式

二,包装器

2-1,function包装器

2-2,bind包装器


一,lambda匿名函数

1-1,lambda的引入

        在C++中,lambda函数是一种简洁的匿名函数或表达式,能够轻松处理复杂的逻辑和数据操作。lambda匿名函数可替换复杂的函数指针或伪函数,可以解决复杂而繁琐的仿函数和函数指针的使用,以及让程序员能够将类似于函数的表达式用作接收函数指针或伪函数的函数的参数(这点与function的作用有关)。      

1-2,lambda表达式书写格式

        lambda匿名函数:[capture-list] (parameters) mutable -> return-type { statement }

        [capture-list]:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量(捕获变量的值或引用‘&’)供lambda函数的函数体使用。

        (parameters):参数列表,与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。

        mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。lambda常性限制的是捕捉列表中的参数,没有限制参数列表中的参数。

        ->returntype:lambda函数的返回值类型(注意:不是表达式返回类型,下面会详细解释这方面),没有返回值时此部分可省略。由于lambda返回值类型相当于使用decltype根据返回值推断得到,如果lambda不包含返回语句,推断出的返回类型将为void,因此返回类型除非必要,一般不需要写。若使用该指定返回类型,参数列表将不可省略(即使参数为空)。

        {statement}:lambda函数的函数体,与普通函数的函数体一样,包含实现功能的代码。

注意:在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为 空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

#include <iostream>
#include <vector>  
#include <algorithm>
using namespace std;
struct Goods
{
    string _name;  // 名字
    double _price; // 价格
    int _evaluate; // 评价
    Goods(const char* str, double price, int evaluate)
        :_name(str)
        , _price(price)
        , _evaluate(evaluate)
    {}
};
struct ComparePriceLess //仿函数
{
    bool operator()(const Goods& gl, const Goods& gr)
    {
        return gl._price < gr._price; 
    }
};
struct ComparePriceGreater //仿函数
{
    bool operator()(const Goods& gl, const Goods& gr)
    {
        return gl._price > gr._price;
    }
};
int main()
{
    vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
   3 }, { "菠萝", 1.5, 4 } };
    //下面两个排序需要编写两个访函数
    sort(v.begin(), v.end(), ComparePriceLess()); //按价格从小到大排序
    sort(v.begin(), v.end(), ComparePriceGreater()); //按价格从大到小排序
    //下面两个排序使用lambda匿名函数,直接一个式子解决一种函数表达,可看出要比伪函数或函数指针(即直接函数实现)更简便
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price < g2._price; });
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {return g1._price > g2._price; });
    /*上面[](const Goods& g1, const Goods& g2) {return g1._price < g2._price; }会自动推导函数提返回类型是bool,即与[](const Goods& g1, const Goods& g2)->bool {return g1._price < g2._price; }等效*/
    return 0;
}

1-3,lambda函数的名称

        上面的lambda使用是作为单独函数表达式,下面将lambda表达式用作名称来使用(将该匿名函数赋给一个变量,该变量就是此名称)。lambda函数表达式必须要使用auto类型来接收。lambda底层实际上是一个类的仿函数,它在C++中并不直接返回它的结果,而是创建了一个可调用的对象(一个函数对象)。因此,你不能直接将一个lambda表达式赋值给一个非函数对象类型的变量(如int)。编译器会报错,因为int类型期望一个int类型或与int型相似类型的值,但是得到了一个lambda表达式(一个类类型)。不仅如此,编译器只有在编译时才可以确定lambda生成的式子,不同编译器下生成的式子还可能还不同,因此只能用auto接收lambda表达式,这里使用typeid().name可看出。

测试一:lambda的调用原理

//调用错误,lambda函数的返回值是int,但本身返回值不是int,是一个类类型
int a = [](int x)->int {return 55 + 5l; };
//auto自动推演类型,调用正确
auto _a = [](int x)->int {return 55 + 5l; };
//赋予lambda函数表达式名称为_a,而lambda底层是类的访函数,按照仿函数的调用即可
int b = _a(5);

//上面lambda的调用相当于下面的类的仿函数调用

class A
{
public:
    int operator()(int x)
    {
        return 55 + 51;
    }
};

A _a;
int b = _a(5);

[](int x)->int {return 55 + 5l; };的调用相当于int x;A()(x);的调用

测试二:lambda原理调用的示例

#include <iostream>
using namespace std;

int main()
{
    int x = 10;
    //使用lambda函数捕获x的引用并修改它  
    auto m = [&x]() {x = x * x; }; //m是一个实现访函数的类
    m(); //lambda函数的调用 
    cout << "x: " << x << endl;  
    cout << typeid(m).name() << endl; 
    return 0;
}

        lambda表达式是一个匿名函数,也可理解为一个表达式,该函数无法直接调用,如果想要直接调用,通常需要借助auto将其赋值给一个变量,通过该名称变量调用。

1-4,lambda捕获列表的使用

        捕获列表说明:捕捉列表用于传入上下文中的数据(传值或引用),以便供lambda使用。这里捕获数据的方式有以下几种:

        [var]:表示值传递方式捕捉变量var

        [&var]:表示引用传递捕捉变量var

        [=]:表示值传递方式捕获当前作用域中所有的变量(包括this)

        [&]:表示引用传递捕获当前作用域中所有的变量(包括this)

        [this]:表示值传递方式捕捉当前的this指针

#include <iostream>
using namespace std;
int main()
{    
    int a = 10, b = 10;
    //值传递捕捉a,捕捉的是a的拷贝
    auto fun1 = [a, b]()mutable { a += 1; b += 1; return a + b; }; /*lambda默认是const函数,限制了捕捉列表的参数,这里要使用mutable取消对捕捉列表的限制*/
    cout << "fun1: a + b = " << fun1() << endl;
    cout << "a + b = " << a + b << endl;
    cout << endl;
    //引用传递捕捉b,捕捉的是b的引用
    auto fun2 = [&a, &b] {a += 1; b += 1; return a + b; }; /*捕捉列表引用捕捉,说明开发者想要对其修改,lambda没有对捕捉列表限制,可以修改*/
    cout << "fun2: a + b = " << fun2() << endl;
    cout << "a + b = " << a + b << endl;
    cout << endl;
    //值传递获取当前作用域所有变量,捕捉所有变量的拷贝
    int c = 10, d = 10;
    auto fun3 = [=]()mutable {c += 1; d += 1; return c + d; }; //与上面“值传递捕捉a”同理
    cout << "fun3: c + d = " << fun3() << endl;
    cout << "c + d = " << c + d << endl;
    cout << endl;
    //引用传递获取当前作用域所有变量,捕捉所有变量的引用
    auto fun4 = [&] {c += 1; d += 1; return c + d; }; //与上面“引用传递捕捉b”同理
    cout << "fun4: c + d = " << fun4() << endl;
    cout << "c + d = " << c + d << endl;
    cout << endl;
    //捕捉a的拷贝,其它数据的引用
    auto fun5 = [&, a]()mutable {a = 1; b = 1; c = 1; return a + b + c; };
    /*auto fun6 = [=, &a]()mutable {a = 1; b = 1; c = 1; return a + b + c; };这里也可捕捉a的引用,其它数据的拷贝*/
    cout << "fun5: a + b + c = " << fun5() << endl;
    cout << "a + b + c = " << a + b + c;
    cout << endl;
    return 0;
}

        这块需说明以下几个注意点:

        1,语法上捕捉列表可由多个捕捉项组成,并以逗号分割,上面最后一个例子运用的就是此原理。还有比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量。

        2,捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复。

        4,在作用域中的lambda函数仅能捕捉当前作用域中局部变量,捕捉任何非此作用域或者
非局部变量都会导致编译报错。

        5,lambda表达式之间不能相互赋值,即使看起来类型相同,因为无论怎样lambda的类型都不可能一样,它的底层是类的仿函数,编译时确定的类型会有所不同。

        6,lambda底层实现了拷贝构造,但是禁掉了默认构造。

#include <iostream>
using namespace std;
void (*PF)(); //函数指针的声明
int main()
{
    auto a = []{cout << "Hello C++" << endl; };
    auto b = []{cout << "Hello C++" << endl; };

    /*a = b; 编译失败,lambda实现虽都一样,但两者的类型不一样,内部不存在不同类型间的赋值。从下面的输出可看出*/
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    cout << endl;
    /*允许使用一个lambda表达式拷贝构造一个新的副本,两者的类型相同,但lambda对象不能默认实现构造*/

    //decltype(a) c; 默认构造的调用,编译失败
    auto c(a);
    cout << typeid(c).name() << endl;
    cout << typeid(a).name() << endl;
    cout << endl;
    c();
    //也可以将lambda表达式赋值给相同类型的函数指针
    PF = a;
    PF();
    return 0;
}

1-5,函数对象与lambda表达式

        C++的函数对象通常指的是仿函数。lambda与函数对象极为相似,实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator(),即仿函数。下面的lambda表达式与函数对象基本可理解为一体。

class Rate
{
public:
    Rate(double rate) : _rate(rate) {}
    double operator()(double money, int year)
    {
        return money * _rate * year;
    }
private:
    double _rate;
};
int main()
{
    //函数对象
    double rate = 0.49;
    Rate r1(rate);
    r1(10000, 2);
    //lamber表达式
    auto r2 = [=](double monty, int year)->double {return monty * rate * year; };
    r2(10000, 2);
    return 0;
}


二,包装器

2-1,function包装器

        function包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。由于函数指针较为复杂,仿函数比较单一,只能实现一种功能,如上Goods的比较,lambda函数语法层中没有类型,auto接收的只是名称,它们各有特色,但比较零散。function包装器的主要作用是封装函数指针(包括普通函数)、函数对象(仿函数)、lambda函数(匿名函数),将它们同一类型,实现一个函数的调用。我们先观察下面代码。

代码一:

#include <iostream>
using namespace std;
template<class F, class T>
T useF(F f, T x)
{
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}
double f(double i)
{
    return i / 2;
}
struct Functor
{
    double operator()(double d) 
    {
        return d / 3;
    }
};
int main()
{
    //函数名(函数指针)
    cout << useF(f, 11.11) << endl;
    cout << endl;
    //函数对象(仿函数)
    cout << useF(Functor(), 11.11) << endl;
    cout << endl;
    //lamber表达式
    cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
    cout << endl;
    return 0;
}

代码二:

#include <iostream>
using namespace std;
template<class F, class T>
T useF(F f, T x)
{
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}
int f1(double i) { return i / 2; }
double f2(double i) { return i / 3; }
double f3(double i) { return i / 4; }
double f4(int i) { return i / 5; }
int main()
{
    /*实例化出的函数返回类型、参数类型相同,输出的count相同,即同一个函数,但若是其中一个类型不同,将会实例化出不同的函数,即输出count不同*/
    cout << useF(f1, 11.11) << endl;
    cout << useF(f2, 11.11) << endl;
    cout << useF(f3, 11.11) << endl;
    cout << useF(f4, 11.11) << endl;
    return 0;
}

        通过上面的程序验证,我们会发现对于函数模板而言,当返回类型和参数类型一致的情况下,编译器不会重新实例化出一份新的函数,但对于函数指针、函数对象、lambda表达式而言,即便三者的函数返回类型、形参类型都相同,但useF函数模板还是实例化了三份。包装器function可以很好的解决上面的问题,将它们封装成一种函数。

std::function在头文件<functional>
类模板结构原型如下
        template <class T> function;    
        template <class Ret, class... Args>
        class function<Ret(Args...)>;
模板参数说明:
        Ret : 被调用函数的返回类型
        Args…:被调用函数的形参类型

使用方法如下:

#include <iostream>
#include<functional>
using namespace std;

template<class F, class T>
T useF(F f, T x)
{
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f1(double i) { return i / 2; }
int f2(double i) { return i / 2; }
double f3(int i) { return i / 3; }

struct Functor
{
    double operator()(double d) { return d / 3; }
};

int main()
{
    //函数指针
    function<double(double)> fc1 = f1;
    fc1(11.11); //将f1封装成fc1,返回类型double、形参类型double
    cout << useF(fc1, 11.11) << endl;
    /*下面的函数指针形式输出发现与上面的不同,返回类型和参数类型只要有一个不同,函数模板实例化出的函数就不同*/
    function<double(int)> fcc1 = f2;  
    fcc1(11.11); //将f2封装成fc,此时返回类型int、形参类型double,与f2不同
    cout << useF(fcc1, 11.11) << endl;
    //封装函数返回类型和形参类型相同,调用函数相同
    function<double(double)> fcc2 = f3;
    fcc2(11.11); //将f3封装成fc2,此时返回类型double、形参类型double,与f3不同
    cout << useF(fcc2, 11.11) << endl;
    cout << endl;

    //函数对象
    function<double(double)> fc2 = Functor();
    fc2(11.11);
    cout << useF(fc2, 11.11) << endl;

    //lambda表达式
    function<double(double)> fc3 = [](double d)->double { return d / 4; };
    fc3(11.11);
    cout << useF(fc3, 11.11) << endl;

    return 0;
}

        function包装器封装时的函数返回类型与形参类型可以与原函数不同,当包装器包装后,此时的调用情况与上面代码二一样,即若函数返回类型和形参类型相同将不会再新实例化出一份函数。

        function包装器包装后的调用与原函数互不影响,如上fc1与fc两者调用的函数不同,这里可放心使用。

类的成员函数的包装

        类的成员函数分为静态成员函数和非静态成员函数。静态成员函数没有包含隐藏的this指针,函数名即为函数地址,包装时跟其它函数包装一样,没有任何变化。非静态成员函数由于第一个参数是隐藏的this指针,所以语法规定包装时第一个形参必须是对象的指针或对象,其次,类的成员函数名本身不是地址,所以这里传递时必须传递地址,即“&”。

#include <iostream>
#include<functional>
using namespace std;
class Plus
{
public:
    static int plusi(int a, int b)
    {
        return a + b;
    }
    double plusd(double a, double b)
    {
        return a + b;
    }
};

int f(int a, int b) { return a + b; }

int main()
{
    //普通函数
    function<int(int, int)> fc1 = f; 
    function<int(int, int)> f1 = &f;
    cout << fc1(1, 1) << "  " << f1(1, 1) << endl;
    cout << f << " " << &f << endl; //输出地址一样,加不加&都行

    //静态成员函数
    function<int(int, int)> fc2 = &Plus::plusi; 
    function<int(int, int)> fc = Plus::plusi;
    cout << fc2(1, 1) << "  " << fc(1, 1) << endl;
    cout << &Plus::plusi << " " << Plus::plusi << endl; //输出地址一样,加不加&都行

    //非静态成员函数
    //非静态成员函数需要对象的指针或者对象去进行调用,因为类的非静态成员函数第一个默认隐形的参数是this指针

    Plus plus;

    //对象指针。这里不能使用Plus::plusd,因为成员函数名不是地址
    function<double(Plus*, double, double)> fc3 = &Plus::plusd; 
    cout << fc3(&plus, 1, 1) << endl;  

    //对象。这里不能使用Plus::plusd

    function<double(Plus, double, double)> fc4 = &Plus::plusd;
    cout << fc4(plus, 1, 1) << "  " << fc4(Plus(), 1, 1) << endl;
    return 0;
}

2-2,bind包装器

        bind是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。它与function底层其实都是仿函数,返回的其实是一个可调用对象。

bind的作用有两个:

        1,调整可调用对象参数的顺序(通常意义不大,了解即可)。

        2,调整可调用对象参数的个数(具有一定的价值)。

bind原型结构如下

形式一:
        template <class Fn, class... Args>
        bind(Fn&& fn, Args&&... args);
形式二:(参数全部指定不能使用function接收,具体下面会说明)
        template <class Ret, class Fn, class... Args>
        bind(Fn&& fn, Args&&... args);

bind调用的一般形式:

1,auto万能接收

auto newCallable = bind(callable, arg_list); 

2,function接收

function<Ret(Args...)> newCallable = bind(callable, arg_list); 

         newCallable:是一个可调用对象,可以是函数、成员函数、函数对象或lambda表达式。

         arg_list:是一个传递给callable,并以逗号分隔的形参列表。若是直接给定 callable 的参数,当我们调用newCallable时,newCallable会调用callable,并传给指定的参数;若是 arg_list 中的参数存在占位符(placeholders占位符),则可以在稍后调用newCallable时提供这些参数。

        占位符placeholders表示参数的“占位”。占位符_1、_2等表示绑定对象被调用时应提供的参数。

#include <iostream>
#include<functional>
using namespace std;

int Sub(int a, int b)
{
    return a - b;
}

class Plus
{
public:
    static int plusi(int a, int b)
    {
        return a + b;
    }

    double plusd(double a, double b)
    {
        return a - b;
    }
};

int main()
{
    //普通函数的正常参数顺序使用
    int x = 10, y = 20;
    auto f1 = bind(Sub, 10, 20);
    cout << f1() << endl; //指定参数,不需要传递

    auto f2 = bind(Sub, placeholders::_1, placeholders::_2); //成员_1表示传递Sub第一个位置参数,_2表示传递第二个位置参数
    cout << f2(x, y) << endl; //占位符使用,x传递绑定对象f2的第一个参数,y传递第二个

    /*调整参数顺序,f2第一个参数接收Sub的第二个位置上的参数,第二个参数接受Sub的第一个位置上的参数(此运用了解一下,意义不大)*/
    function<int(int, int)> f3 = bind(Sub, placeholders::_2, placeholders::_1); 
    cout << f3(x, y) << endl;

    //类的非静态成员函数绑定
    Plus p;
    /*bind绑定Plus::plusd,此处要指名地址(成员函数名称不是地址),具体绑定到p对象上,后面是绑定参数*/
    function<double(double, double)> fc4 = bind(&Plus::plusd, p, placeholders::_1, placeholders::_2);
    cout << fc4(2, 3) << endl;

    function<double(double)> fc5 = bind(&Plus::plusd, Plus(), placeholders::_1, 20); //绑定匿名对象Plus上
    cout << fc5(2) << endl;

    //类的静态成员函数绑定
    auto fc6 = bind(Plus::plusi, 10, 20); //静态成员不属于任何类,无需指名具体对象
//function<int(int, int)>fc6 = bind(Plus::plusi, 5, 7);参数全部指定,此时不能使用function

    cout << fc6() << endl;
    return 0;
}

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

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

相关文章

逻辑这回事(一)----编码规范

说明&#xff1a;优先级是M的规则为强制项&#xff0c;优先级为R的规则为建议项。 通用约束 应有全局观念。 优先级&#xff1a;M 说明&#xff1a;你所编写的代码在成为最终硅片上的一部分之前&#xff0c;需要经过许多设计者利用各种各样的工具进行各种各样的处理。有时&…

【编译原理】LL(1)预测分析法

一、实验目的 LL(1)的含义&#xff1a;第一个L表明自顶向下分析是从左向右扫描输入串&#xff0c;第2个L表明分析过程中将使用最左推导&#xff0c;1表明只需向右看一个符号便可决定如何推导&#xff0c;即选择哪个产生式进行推导。 LL(1) 预测分析方法是确定的自顶向下的语…

[保姆式教程]使用目标检测模型YOLO V8 OBB进行旋转目标的检测:训练自己的数据集(基于卫星和无人机的农业大棚数据集)

最近需要做基于卫星和无人机的农业大棚的旋转目标检测&#xff0c;基于YOLO V8 OBB的原因是因为尝试的第二个模型就是YOLO V8&#xff0c;后面会基于YOLO V9模型做农业大棚的旋转目标检测。YOLO V9目前还不能进行旋转目标的检测&#xff0c;需要修改代码 PS:欢迎大家分享农业大…

Capture One Studio for Mac:打造完美影像的利器

对于摄影师而言&#xff0c;每一次按下快门都是一次对完美影像的追求。而Capture One Studio for Mac正是这样一款能够帮助你实现这一追求的利器。 Capture One Studio for Mac v16.4.2.1中文直装版下载 首先&#xff0c;Capture One Studio for Mac拥有出色的图像处理能力。它…

网络初识 二

一、TCP/IP五层协议 -> 应用层 : 传输的数据在应用程序中如何使用 -> 传输层 : 关注的是通信的起点终点 -> 网络层 : 关注的是通信中的路线规划 -> 数据链路层 : 关注的是相邻节点之间的通信细节 -> 物理层 : 网络通信的基础设施 说是五层,实际上下面…

【Python搞定车载自动化测试】——Python实现CAN总线Bootloader刷写(含Python源码)

系列文章目录 【Python搞定车载自动化测试】系列文章目录汇总 文章目录 系列文章目录&#x1f4af;&#x1f4af;&#x1f4af; 前言&#x1f4af;&#x1f4af;&#x1f4af;一、环境搭建1.软件环境2.硬件环境 二、目录结构三、源码展示1.诊断基础函数方法2.诊断业务函数方法…

GmSSL3.X编译iOS和Android动态库

一、环境准备 我用的Mac电脑编译&#xff0c;Xcode版本15.2&#xff0c;安卓的NDK版本是android-ndk-r21e。 1.1、下载国密源码 下载最新的国密SDK源码到本地。 1.2、安装Xcode 前往Mac系统的AppStore下载安装最新Xcode。 1.3、安卓NDK下载 下载NDK到本地&#xff0c;选…

leetcode每日一题第八十九天

class Solution { public:int subarraySum(vector<int>& nums, int k) {unordered_map<int,int> mp;mp[0] 1;int count 0,pre 0;for(auto x:nums){pre x;if(mp.find(pre-k) ! mp.end()){count mp[pre-k];}mp[pre];}return count;} };

OSM最新台湾电力数据(2024年5月23日数据)转换总结及与(2024年2月16日数据)转换结果对比

osm包含种类繁多&#xff0c;我们这里是只以刚转换的台湾电力设备为例抛砖引玉的进行说明。本次仅仅转换了&#xff0c;变电站、电厂和线路杆塔数据&#xff01; 这种带增强的文件&#xff0c;是我在基础规则之外增加的提取规则。是为了更多的将OSM相关类型数据过滤提取出来&am…

Meta发布Chameleon模型预览,挑战多模态AI前沿

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

LeetCode 279 —— 完全平方数

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 此图利用动态规划进行求解&#xff0c;首先&#xff0c;我们求出小于 n n n 的所有完全平方数&#xff0c;存放在数组 squareNums 中。 定义 dp[n] 为和为 n n n 的完全平方数的最小数量&#xff0c;那么有状态…

基于Llama 3搭建中文版(Llama3-Chinese-Chat)大模型对话聊天机器人

前面两篇博文&#xff0c;我们分别在个人笔记本电脑部署了Llama 3 8B参数大模型&#xff0c;并使用Ollama搭建了基于 Web 可视化对话聊天机器人&#xff0c;可以在自己电脑上愉快的与Llama大模型 Web 机器人对话聊天了。但在使用过程中&#xff0c;笔者发现Llama大模型经常出现…

【IC设计】牛客网-序列检测习题总结

文章目录 状态机基础知识VL25 输入序列连续的序列检测VL26 含有无关项的序列检测VL27 不重叠序列检测VL28 输入序列不连续的序列检测参考资料 状态机基础知识 VL25 输入序列连续的序列检测 timescale 1ns/1ns module sequence_detect(input clk,input rst_n,input a,output re…

乡村振兴的乡村环境治理与保护:加强乡村环境治理与保护,改善乡村环境质量,打造美丽宜居的乡村环境

一、引言 随着乡村振兴战略的深入实施&#xff0c;乡村环境治理与保护成为推动乡村全面振兴的关键环节。乡村环境是乡村发展的重要基础&#xff0c;关系到农民的生产生活和身心健康&#xff0c;也直接影响到乡村经济的可持续发展。因此&#xff0c;加强乡村环境治理与保护&…

Stable Diffusion——U-ViT用于扩散建模的 ViT 主干网

1.概述 扩散模型是最近出现的强大的深度生成模型&#xff0c;可用于生成高质量图像。扩散模型发展迅速&#xff0c;可应用于文本到图像生成、图像到图像生成、视频生成、语音合成和 3D 合成。 除了算法的改进&#xff0c;骨干网的改进在扩散建模中也发挥着重要作用。一个典型…

收集 VSCode 常用快捷键

快速复制行 Shift Alt ↑/↓ 都是往下复制行&#xff0c;区别是&#xff1a;按↓复制时光标会跟着向下移动&#xff0c;按↑复制时光标不移动。 向上/向下移动一行 Alt ↑/↓ 删除整行 Ctrl Shift KCtrl x 剪切快捷键在VSCode 可以直接删除一行 垂直编辑 Ctrl…

Vue 安装vue

1、官网安装下载安装nodejs 2、安装完成后&#xff0c;通过命令查看版本,可以查看到版本 node -v npm -v 3、安装Vue CLi npm install -g vue/cli 4、创建项目,vue create test 如果遇到报错&#xff1a; ERROR Error: spawn yarn ENOENT Error: spawn yarn ENOENT at ChildP…

Flash与EEPROM

文章目录 1. 分类2. 工作原理2.1 擦除操作2.2 写入操作 3. 参考资料 1. 分类 2. 工作原理 在存储数据之前&#xff0c;先擦除存储区域&#xff08;写成全1&#xff09;&#xff0c;进行存储时&#xff0c;将对应位写为0。 注&#xff1a;这里编程不能反向&#xff0c;若写错了…

牛客NC367 第K个n的排列【困难 dfs,全排列问题 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/1595969179464e4c940a90b36abb3c54 思路 全排列问题本文提供的答案在力扣同一道题60. 排列序列&#xff0c;超时了但是截止文章发表日&#xff0c;牛客上是能通过全部测试用例的Java代码 import java.util.*;pu…

vue源码2

vue之mustache库的机理其实是将模板字符串转化为tokens 然后再将 tokens 转化为 dom字符串&#xff0c;如下图 对于一般的将模板字符串转化为dom字符串&#xff0c;这样不能实现复杂的功能 let data {name:小王,age:18 } let templateStr <h1>我叫{{name}},我今年{{ag…